DIYgod/RSSHub · error · Error
Unhandled node type: ${node.nodeType}
Error message
Unhandled node type: ${node.nodeType} What it means
Thrown by the Contentful rich-text renderer in the Netflix newsroom route. The render() function switches on node.nodeType and only handles a fixed set (text, hyperlink, embedded-asset-block, embedded-entry-block, embedded-entry-inline, list-item, unordered-list, ordered-list). Any Contentful node whose nodeType is not one of those cases falls through to default and throws, surfacing the unhandled type name. It is a defensive guard meant to expose schema gaps, not a normal runtime condition.
Source
Thrown at lib/routes/netflix/newsroom.ts:209
}
return `<a href="${embedLink}" target="_blank" rel="noopener noreferrer">${node.data.title ?? embedLink}</a>`;
}
case 'list-item': {
const innerHTML = node.content?.map((c) => render(c)).join('') || '';
return `<li>${innerHTML}</li>`;
}
case 'unordered-list': {
const itemsHTML = node.content?.map((c) => render(c)).join('') || '';
return `<ul>${itemsHTML}</ul>`;
}
case 'ordered-list': {
const itemsHTML = node.content?.map((c) => render(c)).join('') || '';
return `<ol>${itemsHTML}</ol>`;
}
default:
throw new Error(`Unhandled node type: ${node.nodeType}`);
}
};
async function handler(ctx) {
const { category = 'all', region = 'en' } = ctx.req.param();
const baseUrl = 'https://about.netflix.com';
const response = await ofetch(`${baseUrl}/api/data/articles`, {
query: {
language: region,
category: categories[category].id,
},
});
const list = [...response.entities.region.regionArticles, ...response.entities.global.globalArticles].map((i): DataItem & { slug: string } => ({
title: i.title,
link: `${baseUrl}/${region}/news/${i.slug}`,View on GitHub (pinned to bed535e087)
Solutions
- Inspect the thrown node.nodeType value from the error message and add a dedicated case to the switch in lib/routes/netflix/newsroom.ts that renders that node (e.g. case 'paragraph' / 'heading-1' / 'blockquote').
- If the node is non-critical, add a case that returns its inner HTML or empty string so the feed degrades gracefully instead of 500-ing.
- As a stopgap, change the default branch to log the unhandled type via logger and return '' (or node.content mapped through render) so unknown nodes never break the whole feed.
- Report the new nodeType to the route maintainers so the case ships upstream.
Example fix
// before
default:
throw new Error(`Unhandled node type: ${node.nodeType}`);
// after
default:
logger.warn(`Unhandled node type: ${node.nodeType}`);
return node.content?.map((c) => render(c)).join('') ?? ''; Defensive patterns
Strategy: type-guard
Validate before calling
// Before rendering, confirm every node is one the renderer supports.
const HANDLED = new Set(['text','hyperlink','embedded-asset-block','embedded-entry-block','embedded-entry-inline','list-item','unordered-list','ordered-list']);
function assertRenderable(nodes) {
const unknown = [];
for (const n of nodes) {
if (!HANDLED.has(n.nodeType)) unknown.push(n.nodeType);
if (n.content) assertRenderable(n.content);
}
if (unknown.length) logger.warn('Unhandled node types seen:', [...new Set(unknown)]);
} Type guard
const isHandledNode = (n): n is { nodeType: string; content?: any[]; data?: any } =>
typeof n?.nodeType === 'string' && HANDLED.has(n.nodeType); Try / catch
// Wrap the top-level render call so one bad node does not 500 the whole feed.
function safeRender(node) {
try { return render(node); }
catch (e) {
logger.warn('render failed for node type', node?.nodeType, e);
return '';
}
} Prevention
- Never let the Contentful renderer throw from default — log and return '' so unknown nodes degrade gracefully.
- Add unit tests against a sample of real article JSON to catch new nodeTypes before they hit production.
- Pin/monitor the Contentful schema; when editors add block types, extend the switch in the same change.
When it happens
Trigger: A fetched Netflix article body contains a Contentful node type the switch does not recognize — e.g. 'paragraph', 'heading-1', 'heading-2', 'blockquote', 'hr', 'table', 'table-cell', 'entry-hyperlink', or 'asset-hyperlink'. Also triggered if Contentful ships a new block type or editors insert a block (table, divider, embed) that was previously absent from the feed.
Common situations: Contentful editors add a new content block (heading, quote, divider, table) to a newsroom post; Contentful bumps the rich-text schema and adds nodeTypes; the route was written against a limited sample of articles and a new article exercises an unhandled node. The message bubbles up as a 500 to RSSHub consumers.
Related errors
- Unhandled mark type: ${mark.type}
- 无法解析页面数据,请检查漫画 ID 是否正确或页面结构是否变动
- 无法解析页面 Props 数据
- 无法解析页面 HTML 数据,可能触发了反爬策略或页面结构巨变
- 无法在 HTML 缓存中提取核心数据对象
AI-assisted analysis of DIYgod/RSSHub@bed535e087 (2026-08-12).
Data as JSON: /api/errors/ef333efe2d3b9511.
Report an issue: GitHub.