DIYgod/RSSHub · warning · Error

Unhandled widget type: ${w.type}

Error message

Unhandled widget type: ${w.type}

What it means

Thrown by sustainabilitymag/articles.ts:61 via `throw new Error(...)` in the default arm of the render(widgets) switch, when an article body widget has a type other than text, blockquote, keyFacts, inlineVideo, inlineImage. The article body is a list of widgets fetched from the site's GraphQL API, so a new widget type introduced upstream surfaces here. NB: the adjacent inlineVideo branch (line 52) has a latent bug — `new Error(...)` is constructed but NOT thrown, so an unhandled video provider silently returns an Error object as the rendered HTML instead of throwing; worth fixing alongside any default-arm change.

Source

Thrown at lib/routes/sustainabilitymag/articles.ts:61

                case 'text':
                    return w.html;
                case 'blockquote':
                    return `<blockquote>${w.html}</blockquote>`;
                case 'keyFacts':
                    return `<div><ul>${w.keyFacts.map((k) => `<li>${k.text}</li>`).join('')}</ul></div>`;
                case 'inlineVideo':
                    return w.provider === 'youtube'
                        ? `<iframe id="ytplayer" type="text/html" width="640" height="360" src="https://www.youtube-nocookie.com/embed/${w.videoId}" frameborder="0" allowfullscreen referrerpolicy="strict-origin-when-cross-origin"></iframe>`
                        : new Error(`Unhandled inlineVideo provider: ${w.provider}`);
                case 'inlineImage':
                    return w.inlineImageImages
                        .map((image) => {
                            const i = image.images[findLargestImgKey(image.images)][0];
                            return renderFigure(i.url, i.caption);
                        })
                        .join('');
                default:
                    throw new Error(`Unhandled widget type: ${w.type}`);
            }
        })
        .join('');

async function handler() {
    const baseURL = 'https://sustainabilitymag.com';
    const feedURL = `${baseURL}/articles`;
    const feedLang = 'en' as Language;
    const feedDescription = 'Sustainability Magazine Articles';

    const requestEndpoint = `${baseURL}/graphql`;
    const requestBody = JSON.stringify({
        query: /* GraphQL */ `
            query PaginatedQuery($url: String!, $page: Int = 1, $widgetType: String!) {
                paginatedWidget(url: $url, widgetType: $widgetType) {
                    ... on SimpleArticleGridWidget {
                        articles(page: $page) {
                            results {

View on GitHub (pinned to bed535e087)

Solutions

  1. Open the failing article URL in a browser and inspect its __NEXT_DATA__ script to find the unknown w.type in article.body.widgets.
  2. Add a `case '<type>':` for the new widget in the render switch (map it to appropriate HTML, or fall back to w.html if available).
  3. As a stopgap to keep the feed alive, change the default arm to `return '';` (skip unknown widgets) instead of throwing — then track the missing type separately.

Example fix

// before
default:
    throw new Error(`Unhandled widget type: ${w.type}`);
// after (defensive: skip unknown widgets so the feed stays up)
default:
    return '';
Defensive patterns

Strategy: try-catch

Validate before calling

// Best handled inside render(): only attempt known widget types
const KNOWN_WIDGET_TYPES = new Set(['text', 'blockquote', 'keyFacts', 'inlineVideo', 'inlineImage']);

function hasOnlyKnownWidgets(widgets: { type: string }[]): boolean {
  return widgets.every((w) => KNOWN_WIDGET_TYPES.has(w.type));
}

Type guard

const KNOWN_WIDGET_TYPES = new Set(['text', 'blockquote', 'keyFacts', 'inlineVideo', 'inlineImage']);

function isKnownWidgetType(type: string): boolean {
  return KNOWN_WIDGET_TYPES.has(type);
}

Try / catch

// Wrap per-article rendering so one bad widget does not kill the feed.
try {
  item.description = render(article.body.widgets);
} catch (e) {
  // log the unknown type, fall back to a minimal description
  item.description = article.headline ?? '';
}

Prevention

When it happens

Trigger: The magazine ships a new content-block type (e.g. embed, gallery, tweet, pullQuote, divider); a sponsored/paid widget ('sell') with a custom type appears; a CMS migration renames an existing widget type between deploys.

Common situations: Site redesign or CMS migration rolls out after the route was written; a premium/sponsored article uses a widget type absent from normal editorial articles.

Related errors


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