DIYgod/RSSHub · error · Error
Unsupported element type: ${element.type}
Error message
Unsupported element type: ${element.type} What it means
The RFA (Radio Free Asia) route renders structured `content_elements` from a JSON Fusion cache embedded in the page. `renderElement` is a switch over known element types (text, list, gallery, quote, raw_html, custom_embed). The `default` branch throws a generic `Error` with the unrecognized `element.type` whenever the upstream introduces a new element kind the renderer does not know about. This is a defensive exhaustiveness guard, not user input.
Source
Thrown at lib/routes/rfa/index.ts:63
const stream = element.streams?.find((s) => s.stream_type === 'mp4');
return stream ? `<figure><video controls src="${stream.url}" poster="${element.promo_image?.url ?? ''}"></video>${element.headlines?.basic ? `<figcaption>${element.headlines.basic}</figcaption>` : ''}</figure>` : '';
}
case 'oembed_response':
return element.raw_oembed?.html ?? '';
case 'list': {
const tag = element.list_type === 'ordered' ? 'ol' : 'ul';
return `<${tag}>${element.items.map((el) => `<li>${el.content}</li>`).join('')}</${tag}>`;
}
case 'gallery':
return element.content_elements.map((el) => renderElement(el)).join('');
case 'quote':
return `<blockquote>${element.content_elements.map((el) => renderElement(el)).join('')}${element.citation?.content ? `<cite>${element.citation.content}</cite>` : ''}</blockquote>`;
case 'raw_html':
return element.content;
case 'custom_embed':
return '';
default:
throw new Error(`Unsupported element type: ${element.type}`);
}
};
async function handler(ctx: Context) {
const { language = 'english', channel, subChannel } = ctx.req.param();
const baseUrl = 'https://www.rfa.org';
const link = `${baseUrl}/${[language, channel, subChannel].filter(Boolean).join('/')}/`;
const response = await ofetch(link);
const $ = load(response);
const contentCache = JSON.parse(response.match(/Fusion\.contentCache=(\{.*?\});Fusion\.layout/)?.[1] ?? '{}');
const list = new Map<string, any>(
Object.values<any>(contentCache['content-api-collections'] ?? {})
.flatMap((entry) => entry.data?.content_elements ?? [])
.map((story) => [story._id, story])
)
.values()View on GitHub (pinned to bed535e087)
Solutions
- Inspect the value of `element.type` from the error message, then add a `case` to `renderElement` in lib/routes/rfa/index.ts that maps it to the appropriate HTML (often `<div>${renderChildren}</div>` or a passthrough of `element.content`).
- If the new type is rare or has no useful representation, add a `case` that returns `''` (like `custom_embed`) instead of falling through to the throw.
- Re-fetch the failing article after the patch to confirm the feed builds.
Example fix
// before
case 'raw_html':
return element.content;
case 'custom_embed':
return '';
default:
throw new Error(`Unsupported element type: ${element.type}`);
// after
case 'raw_html':
return element.content;
case 'custom_embed':
return '';
case 'video':
return `<video src="${element.url}" controls></video>`;
default:
return ''; Defensive patterns
Strategy: type-guard
Validate before calling
const KNOWN_TYPES = new Set(['text', 'header', 'list', 'gallery', 'quote', 'raw_html', 'custom_embed']);
if (!KNOWN_TYPES.has(element.type)) {
// log and return empty string instead of throwing
return '';
} Type guard
const isKnownElementType = (t: unknown): boolean =>
typeof t === 'string' && KNOWN_TYPES.has(t); Try / catch
try {
html = renderElement(el);
} catch (e) {
if (e instanceof Error && e.message.startsWith('Unsupported element type')) {
html = '';
} else throw e;
} Prevention
- Add new cases to renderElement proactively whenever the upstream CMS ships a new block type.
- Log the unknown type so you learn about schema drift before users do.
- Replace the `default` throw with a soft fallback for production resilience.
When it happens
Trigger: The RFA CMS ships a new content_element type (e.g. `video`, `audio`, `table`, `tweet`) not covered by the switch. Any article containing that element triggers the throw on feed generation. The error is data-driven, not parameter-driven.
Common situations: Site redesign or CMS upgrade on rfa.org adds a previously-unseen block type; an article embeds a social post or live stream that the renderer never had to handle before.
Related errors
- Instagram: Unhandled feed type: ${productType}
- Instagram: Unhandled carousel type: ${_type}
- Instagram: Unhandled feed type: ${type}
- Unable to decode the Locals server response.
- Unable to resolve the Locals community metadata.
AI-assisted analysis of DIYgod/RSSHub@bed535e087 (2026-08-12).
Data as JSON: /api/errors/5fd5f265afa4e12c.
Report an issue: GitHub.