DIYgod/RSSHub · error · Error

Unable to fetch message feed from this channel. Please check

Error message

Unable to fetch message feed from this channel. Please check this URL to see if you can view the message preview: ${resourceUrl}

What it means

After scraping a Telegram channel preview page (t.me/s/<channel>), the parser looks for .tgme_widget_message_wrap entries (optionally excluding service messages). If it finds zero message wraps AND zero .tgme_channel_history containers, it concludes the channel feed is not viewable and tells the user to open the URL to confirm.

Source

Thrown at lib/routes/telegram/channel.ts:236

     */
    $('a[onclick][href]').each((_, elem) => {
        const $elem = $(elem);
        const href = $elem.attr('href');
        href && $elem.attr('href', href.replaceAll('&amp;', '&'));
    });

    !showHashtagAsHyperlink &&
        $('a[href^="?q=%23"]').each((_, elem) => {
            const $elem = $(elem);
            $elem.replaceWith($elem.text());
        });

    const list = includeServiceMsg
        ? $('.tgme_widget_message_wrap:not(.tgme_widget_message_wrap:has(.tme_no_messages_found))') // exclude 'no posts found' messages
        : $('.tgme_widget_message_wrap:not(.tgme_widget_message_wrap:has(.service_message,.tme_no_messages_found))'); // also exclude service messages

    if (list.length === 0 && $('.tgme_channel_history').length === 0) {
        throw new Error(`Unable to fetch message feed from this channel. Please check this URL to see if you can view the message preview: ${resourceUrl}`);
    }

    const channelName = $('.tgme_channel_info_header_title').text();
    const feedTitle = (searchQuery ? `"${searchQuery}" - ` : '') + channelName + ' - Telegram Channel';

    return {
        title: feedTitle,
        description: $('.tgme_channel_info_description').text(),
        link: resourceUrl,
        allowEmpty: true,

        itunes_author: channelName,
        image: $('.tgme_page_photo_image > img').attr('src'),

        item: list
            .toArray()
            .map((item) => {
                const $item = $(item);

View on GitHub (pinned to bed535e087)

Solutions

  1. Open resourceUrl in a browser to confirm the channel is publicly viewable; if it requires login, the public RSS cannot reach it.
  2. Verify the tgme_widget_message_wrap / tgme_channel_history selectors still match the current Telegram preview markup.
  3. Use a cleaner IP or proxy if Telegram is serving an interstitial instead of the widget HTML.
  4. For a legitimately private channel, configure TELEGRAM_SESSION and use the MTProto-based media route instead of the public scraper.

Example fix

// before
if (list.length === 0 && $('.tgme_channel_history').length === 0) {
    throw new Error(`Unable to fetch message feed from this channel. Please check this URL to see if you can view the message preview: ${resourceUrl}`);
}

// after: distinguish 'private/login wall' from 'selector drift' for clearer errors
if (list.length === 0 && $('.tgme_channel_history').length === 0) {
    if ($('.tme_no_messages_found, .tgme_widget_login').length > 0) {
        throw new Error(`Channel is private, restricted, or requires login: ${resourceUrl}`);
    }
    throw new Error(`No message containers found; Telegram preview markup may have changed: ${resourceUrl}`);
}
Defensive patterns

Strategy: validation

Validate before calling

function isChannelViewable($: cheerio.Root): boolean {
    return $('.tgme_channel_history').length > 0 || $('.tgme_widget_message_wrap').length > 0;
}
// before building the feed:
// if (!isChannelViewable($)) { surface a clear access error }

Type guard

function hasPublicPreview($: cheerio.CheerioAPI): boolean {
    return $('.tgme_channel_history').length > 0 || $('.tgme_widget_message_wrap:not(:has(.tme_no_messages_found))').length > 0;
}

Try / catch

try {
    return await buildFeed(html, resourceUrl);
} catch (e) {
    if (e instanceof Error && /Unable to fetch message feed/.test(e.message)) {
        // suggest the MTProto route if TELEGRAM_SESSION is configured
        return suggestMtprotoRoute(resourceUrl);
    }
    throw e;
}

Prevention

When it happens

Trigger: The channel is private (no public preview), restricted/geo-blocked, deleted, or Telegram returned a 'tme_no_messages_found'/login wall instead of the widget HTML. The scraper got a page but no recognizable message containers.

Common situations: Subscribing to a private channel that has no public t.me/s view; the channel was banned or deleted; the requesting IP hit a rate-limit/login interstitial; tgme markup changed and selectors no longer match.

Related errors


AI-assisted analysis of DIYgod/RSSHub@bed535e087 (2026-08-12). Data as JSON: /api/errors/788cc8b1333c55f5. Report an issue: GitHub.