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

  1. 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`).
  2. 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.
  3. 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

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


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