Running get_posts within get_posts to get children of children

Your code should work, it might be that your posts per page option in back end is set to 1. It would be beneficial to explicitely set posts_per_page in your query args. You can also make the code faster if you only need to know the amount of grandchild posts, you can just return the post ids’s.

Here is an example: (Copied and modified from your code)

$postid = get_the_ID();
$kids = array(
    'posts_per_page' => -1,
    'post_type'      => 'courses',
    'post_parent'    => $postid
);
$children = get_posts( $kids );
if ( $children ) {
    echo '<ul>';
    foreach( $children as $child ) {
        $childid     = $child->ID;
        $coursetitle = get_the_title( $childid );
        $grandkids   = get_posts( array(
            'posts_per_page' => -1,
            'fields'         => 'ids', // Only get post id's, much faster
            'post_type'      => 'courses',
            'post_parent'    => $childid
        ) );
        $grandchildren = count( $grandkids );
        echo '<li>';
        echo $coursetitle;
        echo $grandchildren . 'courses available';
        echo '</li>';
    }
}

If that does not work, you most probably have something intefering with your query, maybe a bad pre_get_posts action somewhere. To test this out, add remove_all_actions( 'pre_get_posts' ); before your query.

You can also have a look at get_pages() to run your queries which will not be altered by the normal WP_Query filters and actions