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('&', '&'));
});
!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
- Open resourceUrl in a browser to confirm the channel is publicly viewable; if it requires login, the public RSS cannot reach it.
- Verify the tgme_widget_message_wrap / tgme_channel_history selectors still match the current Telegram preview markup.
- Use a cleaner IP or proxy if Telegram is serving an interstitial instead of the widget HTML.
- 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
- Confirm the channel is publicly viewable at t.me/s/<channel> before subscribing.
- Keep the tgme_* selectors in sync with the current Telegram preview markup.
- Use a clean IP/proxy to avoid login interstitials on the public preview.
- For private channels, configure TELEGRAM_SESSION and prefer the MTProto-based routes.
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
- No response body
- No article body
- Cannot start TG: ${err}
- Failed to fetch thread data
- Failed to fetch Wikipedia current events: ${message}
AI-assisted analysis of DIYgod/RSSHub@bed535e087 (2026-08-12).
Data as JSON: /api/errors/788cc8b1333c55f5.
Report an issue: GitHub.