get_the_tag_list() returns bad links

The reason for your problem is that, by default, custom post types are excluded from the main query. As there are no “normal” posts assigned to the specific tag, you get a 404 page when trying to view the specific tag’s page

In order for this to work, you’ll need to add your custom post type to the main query before it executes. To accomplish this, you’ll need to use pre_get_posts. If you just want to target the tag archive page, you can make use of the conditional tag is_tag()

The following code in your functions.php will work

function custom_post_type_tags( $query ) {
    if ( !is_admin() && $query->is_tag() && $query->is_main_query() ) {
        $query->set( 'post_type', array( 'post', 'portfolio' ) );
    }
}
add_action( 'pre_get_posts', 'custom_post_type_tags' );

EDIT

If you need to display your custom post type in all templates, you can just remove the is_tag() condition

function custom_post_type_tags( $query ) {
    if ( !is_admin() && $query->is_main_query() ) {
        $query->set( 'post_type', array( 'post', 'portfolio' ) );
    }
}
add_action( 'pre_get_posts', 'custom_post_type_tags' );