DIYgod/RSSHub · error · Error

Failed to fetch episode list from Castbox

Error message

Failed to fetch episode list from Castbox

What it means

Thrown by the Castbox channel route when the episode-list endpoint `https://everest.castbox.fm/data/episode_list/v2` returns a body whose `.data.episode_list` is missing or falsy. Distinct from the channel-data guard (it fires after channel data is confirmed). Plain `Error`.

Source

Thrown at lib/routes/castbox/channel.ts:77

        const channelParams = { cid, r: 1, raw: 1, web: 1 };
        const { m: cm, n: cn, queryStr: cQuery } = getNonce(channelParams);

        const channelData = await ofetch(`https://everest.castbox.fm/data/channel/v3?${cQuery}&m=${cm}&n=${cn}`);

        if (!channelData?.data) {
            throw new Error('Failed to fetch channel data from Castbox');
        }

        const chData = channelData.data;
        const limit = ctx.req.query('limit') ? Number(ctx.req.query('limit') as string) : 50;

        const epParams = { cid, limit, r: 1, raw: 1, web: 1 };
        const { m: em, n: en, queryStr: eQuery } = getNonce(epParams);

        const epData = await ofetch(`https://everest.castbox.fm/data/episode_list/v2?${eQuery}&m=${em}&n=${en}`);

        if (!epData?.data?.episode_list) {
            throw new Error('Failed to fetch episode list from Castbox');
        }

        const episodes = epData.data.episode_list;

        const items = episodes.map((ep: any) => {
            let enclosure_type = 'audio/mpeg';
            if (ep.video === 1 || ep.url?.includes('.mp4')) {
                enclosure_type = 'video/mp4';
            } else if (ep.url?.includes('.m4a')) {
                enclosure_type = 'audio/mp4';
            }

            return {
                title: ep.title,
                description: ep.description,
                pubDate: parseDate(ep.release_date),
                link: `https://castbox.fm/episode/${encodeURIComponent(ep.title)}-id${cid}-id${ep.eid}`,
                enclosure_url: ep.url,

View on GitHub (pinned to bed535e087)

Solutions

  1. Confirm the channel actually has episodes on castbox.fm.
  2. Retry once to rule out transient API hiccup.
  3. If all channels fail, the `getNonce` params (`{ cid, limit, r: 1, raw: 1, web: 1 }`) likely no longer match Castbox's expected signature — update them.
  4. Dump the raw episode_list response body to see whether Castbox returns an error code/message.
Defensive patterns

Strategy: try-catch

Type guard

function hasEpisodeList(r: unknown): r is { data: { episode_list: unknown[] } } {
    return Boolean(r && typeof r === 'object' && (r as any).data?.episode_list);
}

Try / catch

try {
    const epData = await ofetch(episodeUrl);
    if (!epData?.data?.episode_list) {
    // retry once, then throw with the upstream body for diagnosis
    }
} catch (e) {
    throw new Error(`Castbox episode API unreachable: ${(e as Error).message}`);
}

Prevention

When it happens

Trigger: Channel exists but has zero episodes and Castbox omits the `episode_list` key entirely; API version bump removes `episode_list`; nonce rejection on the second call; `limit` query param out of accepted range causes Castbox to return an error envelope.

Common situations: Newly created channels with no published episodes, channels that are video-only on a different endpoint, or a breaking change in Castbox's `/episode_list/v2` response schema.

Related errors


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