DIYgod/RSSHub · warning · Error

Unknown category: . Supported

Error message

Unknown category: ${category}. Supported: ${Object.keys(APP_CATEGORY_MAP).join(', ')}

What it means

The theinitium app route maps the :category path param through APP_CATEGORY_MAP to a {channelType, language} pair. If the provided category is not a key in that map, it throws an Error listing the supported keys, so the caller knows which values are accepted.

Solutions

  1. Use one of the categories listed in the error message (the keys of APP_CATEGORY_MAP).
  2. Omit the category to fall back to the default 'latest_sc'.
  3. If a real channel is missing, add it to APP_CATEGORY_MAP with the correct channelType and language.

Example fix

// before
const mapping = APP_CATEGORY_MAP[category];
if (!mapping) {
    throw new Error(`Unknown category: ${category}. Supported: ${Object.keys(APP_CATEGORY_MAP).join(', ')}`);
}

// after: throw an InvalidParameterError (RSS idiom) and hint the default
const mapping = APP_CATEGORY_MAP[category];
if (!mapping) {
    throw new InvalidParameterError(`Unknown category "${category}". Supported: ${Object.keys(APP_CATEGORY_MAP).join(', ')} (default: latest_sc)`);
}
Defensive patterns

Strategy: validation

Validate before calling

const SUPPORTED = new Set(Object.keys(APP_CATEGORY_MAP));
function isSupportedCategory(c: string): boolean {
    return SUPPORTED.has(c);
}
// expose valid categories via the route docs; default to 'latest_sc'

Type guard

function isAppCategory(c: string): c is keyof typeof APP_CATEGORY_MAP {
    return c in APP_CATEGORY_MAP;
}

Try / catch

try {
    return await handler(ctx);
} catch (e) {
    if (e instanceof Error && /Unknown category/.test(e.message)) {
        return badRequest(`Use one of: ${Object.keys(APP_CATEGORY_MAP).join(', ')}`);
    }
    throw e;
}

Prevention

When it happens

Trigger: Requesting /theinitium/app/<category> with a category that is not one of the predefined keys (e.g. an old or typo'd channel name). The default category is 'latest_sc'.

Common situations: User copies a channel name from the website that is not in APP_CATEGORY_MAP; the map was refactored and a previously-valid category was removed/renamed; typo in the URL.

Related errors


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

Appendix: source

Thrown at lib/routes/theinitium/app.ts:104

| 速递  | whats\\_new\\_sc    | whats\\_new\\_tc    |
| 专题  | report\\_sc        | report\\_tc        |
| 评论  | opinion\\_sc       | opinion\\_tc       |
| 国际  | international\\_sc | international\\_tc |
| 大陆  | mainland\\_sc      | mainland\\_tc      |
| 香港  | hongkong\\_sc      | hongkong\\_tc      |
| 台湾  | taiwan\\_sc        | taiwan\\_tc        |

::: tip
原 App 路由已迁移至 Ghost CMS API。播客(article\\_audio)分类已停用,请改用 \`/theinitium/channel\` 路由。
:::`,
};

async function handler(ctx) {
    const category = ctx.req.param('category') ?? 'latest_sc';

    const mapping = APP_CATEGORY_MAP[category];
    if (!mapping) {
        throw new Error(`Unknown category: ${category}. Supported: ${Object.keys(APP_CATEGORY_MAP).join(', ')}`);
    }

    const { channelType, language } = mapping;

    // Reason: Reuse the same Ghost tag filter logic as processFeed's channel case
    const baseTag = CHANNEL_TAG_MAP[channelType] ?? channelType;
    let filter = '';
    if (baseTag === '') {
        // "latest" = no tag filter, just filter by language via internal tag
        filter = `tag:hash-${language}`;
    } else {
        const tagSlug = applyLanguageToTagSlug(baseTag, language);
        filter = `tag:${tagSlug}`;
    }

    const params: Record<string, string> = {
        include: 'tags,authors',
        limit: '20',

View on GitHub (pinned to bed535e087)