DIYgod/RSSHub · error · InvalidParameterError

Invalid Syosetu subsite.\nValid subsites are: yomou, noc, mn

Error message

Invalid Syosetu subsite.\nValid subsites are: yomou, noc, mnlt, mid

What it means

Thrown by syosetu/search.ts:120 as an InvalidParameterError in the default arm of createNovelSearchBuilder, when `sub` is not 'yomou' (handled earlier, returns a general SearchBuilder) and not one of the R18 cases {noc, mnlt, mid}. CRITICAL footgun: the SyosetuSub enum in types/search.ts has only FOUR members (YOMOU, NOCTURNE, MOONLIGHT, MIDNIGHT) and does NOT include MOONLIGHT_BL — so /syosetu/search/mnlt-bl/... throws here, even though mnlt-bl is a valid subsite in the rankingr18 route. The same-named enum diverges between files.

Source

Thrown at lib/routes/syosetu/search.ts:120

    const r18Params = { ...searchParams };

    switch (sub) {
        case SyosetuSub.NOCTURNE:
            r18Params.nocgenre = R18Site.Nocturne;
            break;
        case SyosetuSub.MOONLIGHT:
            // If either 女性向け/BL is chosen, nocgenre will be in query string
            // If no specific genre selected, include both
            if (!r18Params.nocgenre) {
                r18Params.nocgenre = [R18Site.MoonLight, R18Site.MoonLightBL].join('-') as Join<R18Site>;
            }
            break;
        case SyosetuSub.MIDNIGHT:
            r18Params.nocgenre = R18Site.Midnight;
            break;
        default:
            throw new InvalidParameterError('Invalid Syosetu subsite.\nValid subsites are: yomou, noc, mnlt, mid');
    }

    return new SearchBuilderR18(r18Params, new NarouNovelFetch());
}

async function handler(ctx: Context): Promise<Data> {
    const { sub, query } = ctx.req.param();
    const searchUrl = `https://${sub}.syosetu.com/search/search/search.php?${query}`;

    const limit = Math.min(Number(ctx.req.query('limit') ?? 40), 40);
    const searchParams = mapToSearchParams(query, limit);
    const builder = createNovelSearchBuilder(sub, searchParams);
    const result = await builder.execute();

    const items = result.values.map((novel) => ({
        title: novel.title,
        link: `https://${isGeneral(sub) ? 'ncode' : 'novel18'}.syosetu.com/${String(novel.ncode).toLowerCase()}`,
        description: renderDescription({ novel, genreText: GenreNotation[novel.genre] }),

View on GitHub (pinned to bed535e087)

Solutions

  1. Use yomou, noc, mnlt, or mid as the sub.
  2. For Moonlight BL results, use sub=mnlt and supply the nocgenre parameter in the query string.

Example fix

// before (mnlt-bl is not a valid search subsite)
GET /syosetu/search/mnlt-bl/word=...
// after (use mnlt + nocgenre in query)
GET /syosetu/search/mnlt/word=...&nocgenre=<MoonLightBL>
Defensive patterns

Strategy: validation

Validate before calling

import { SyosetuSub } from './types/search';

// search.ts SyosetuSub is {yomou, noc, mnlt, mid} — mnlt-bl is NOT valid here.
function isValidSearchSub(sub: string): boolean {
  return Object.values(SyosetuSub).includes(sub as SyosetuSub);
}

Type guard

import { SyosetuSub } from './types/search';

function isSearchSub(sub: string): sub is SyosetuSub {
  return ['yomou', 'noc', 'mnlt', 'mid'].includes(sub);
}

Prevention

When it happens

Trigger: /syosetu/search/<sub>/<query> with sub not in {yomou, noc, mnlt, mid}: specifically mnlt-bl throws; full names like 'nocturne' or 'moonlight'; typos.

Common situations: Trying to search Moonlight BL via this route (not supported — you must use mnlt and pass nocgenre in the query string); assuming mnlt-bl works here because it works in rankingr18; using the display name.

Related errors


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