how to use custom loop in the_content filter

the_content is expecting a string, you’ll have to use output buffering:

function my_custom_loop() {

$taxonomy = 'state_cat';

  $terms = get_the_terms($post->ID, $taxonomy);

  if ($terms && ! is_wp_error($terms)) : 


$terms_array = array();

foreach ($terms as $term) {
  $terms_array[] = $term->slug;
}

$have_you_read_query = new WP_Query(
  array(
    'posts_per_page' => 100,
    'post_type' => 'post',
    'post__not_in' => array($post->ID),
    'category__not_in' => 418,
    'tax_query' => array(
      array(
        'taxonomy' => $taxonomy,
        'field' => 'slug',
   'include_children' => false,
        'terms' => $terms_array
      )
    )
  )
);
 //Stores html in buffer
 ob_start();
 if($my_query->have_posts()) :


   while($my_query->have_posts()) : $my_query->the_post(); ?>

    <div class="custom-loop clearfix">
            <div class="loop-thumb"><?php the_post_thumbnail('thumbnail'); ?></div>
            <div class="loop-content">
                <div class="loop-title"><?php the_title(); ?></div>
                <div class="loop-excerpt"><?php the_excerpt(); ?></div>
            </div>
    </div>
  <?php endwhile; wp_reset_postdata(); 


   endif; 

 endif; 
 //get buffer contents
 $output = ob_get_clean();
 return $output;
}