{"record":{"id":"b8bdf715573cb53f","repo":"DIYgod/RSSHub","slug":"unhandled-widget-type-w-type","errorCode":null,"errorMessage":"Unhandled widget type: ${w.type}","messagePattern":"Unhandled widget type: (.+?)","errorType":"exception","errorClass":"Error","httpStatus":503,"severity":"warning","filePath":"lib/routes/sustainabilitymag/articles.ts","lineNumber":61,"sourceCode":"                case 'text':\n                    return w.html;\n                case 'blockquote':\n                    return `<blockquote>${w.html}</blockquote>`;\n                case 'keyFacts':\n                    return `<div><ul>${w.keyFacts.map((k) => `<li>${k.text}</li>`).join('')}</ul></div>`;\n                case 'inlineVideo':\n                    return w.provider === 'youtube'\n                        ? `<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>`\n                        : new Error(`Unhandled inlineVideo provider: ${w.provider}`);\n                case 'inlineImage':\n                    return w.inlineImageImages\n                        .map((image) => {\n                            const i = image.images[findLargestImgKey(image.images)][0];\n                            return renderFigure(i.url, i.caption);\n                        })\n                        .join('');\n                default:\n                    throw new Error(`Unhandled widget type: ${w.type}`);\n            }\n        })\n        .join('');\n\nasync function handler() {\n    const baseURL = 'https://sustainabilitymag.com';\n    const feedURL = `${baseURL}/articles`;\n    const feedLang = 'en' as Language;\n    const feedDescription = 'Sustainability Magazine Articles';\n\n    const requestEndpoint = `${baseURL}/graphql`;\n    const requestBody = JSON.stringify({\n        query: /* GraphQL */ `\n            query PaginatedQuery($url: String!, $page: Int = 1, $widgetType: String!) {\n                paginatedWidget(url: $url, widgetType: $widgetType) {\n                    ... on SimpleArticleGridWidget {\n                        articles(page: $page) {\n                            results {","sourceCodeStart":43,"sourceCodeEnd":79,"githubUrl":"https://github.com/DIYgod/RSSHub/blob/bed535e0879dc71c5aff6f1e7bd1ac21ede40115/lib/routes/sustainabilitymag/articles.ts#L43-L79","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Open the failing article URL in a browser and inspect its __NEXT_DATA__ script to find the unknown w.type in article.body.widgets.","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).","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."],"exampleFix":"// before\ndefault:\n    throw new Error(`Unhandled widget type: ${w.type}`);\n// after (defensive: skip unknown widgets so the feed stays up)\ndefault:\n    return '';","handlingStrategy":"try-catch","validationCode":"// Best handled inside render(): only attempt known widget types\nconst KNOWN_WIDGET_TYPES = new Set(['text', 'blockquote', 'keyFacts', 'inlineVideo', 'inlineImage']);\n\nfunction hasOnlyKnownWidgets(widgets: { type: string }[]): boolean {\n  return widgets.every((w) => KNOWN_WIDGET_TYPES.has(w.type));\n}","typeGuard":"const KNOWN_WIDGET_TYPES = new Set(['text', 'blockquote', 'keyFacts', 'inlineVideo', 'inlineImage']);\n\nfunction isKnownWidgetType(type: string): boolean {\n  return KNOWN_WIDGET_TYPES.has(type);\n}","tryCatchPattern":"// Wrap per-article rendering so one bad widget does not kill the feed.\ntry {\n  item.description = render(article.body.widgets);\n} catch (e) {\n  // log the unknown type, fall back to a minimal description\n  item.description = article.headline ?? '';\n}","preventionTips":["Change the render() default arm to return '' (skip) instead of throw, so unknown widget types degrade gracefully.","Fix the latent bug at line 52: `new Error(...)` should be `throw new Error(...)` (or handled explicitly).","Log unknown widget types so maintainers learn about upstream additions quickly."],"tags":["unhandled-case","upstream-change","content-parsing","sustainabilitymag"],"backgroundTag":null,"analyzedSha":"bed535e0879dc71c5aff6f1e7bd1ac21ede40115","analyzedAt":"2026-08-12T19:29:35.364Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}