DIYgod/RSSHub · error · Error

User ID is required for non-posts sources

Error message

User ID is required for non-posts sources

What it means

Thrown inside buildApiUrl() when the source is not 'posts' (and not the discord-with-userId case) and no userId was provided. The Kemono API requires a user identifier for all per-creator endpoints (e.g. /patreon/user/{userId}/posts), so a missing userId makes the URL unbuildable. This is a programming/contract error in how the route is invoked, not a network condition.

Source

Thrown at lib/routes/kemono/index.tsx:101

            parsedData = JSON.parse(parsedData);
        }
        return parsedData;
    } catch {
        return field;
    }
}

function buildApiUrl(source: string, userId?: string, contentType?: string): string {
    if (source === 'posts') {
        return `${KEMONO_API_URL}/posts`;
    }

    if (source === 'discord' && userId) {
        return `${KEMONO_API_URL}/discord/channel/lookup/${userId}`;
    }

    if (!userId) {
        throw new Error('User ID is required for non-posts sources');
    }

    const basePath = `${KEMONO_API_URL}/${source}/user/${userId}`;
    return contentType ? `${basePath}/${contentType}` : `${basePath}/posts`;
}

function buildFrontendUrl(source: string, userId?: string, contentType?: string): string {
    if (source === 'posts') {
        return `${KEMONO_ROOT_URL}/posts`;
    }

    if (source === 'discord' && userId) {
        return `${KEMONO_ROOT_URL}/${source}/server/${userId}`;
    }

    if (!userId) {
        throw new Error('User ID is required for non-posts sources');
    }

View on GitHub (pinned to bed535e087)

Solutions

  1. Ensure the route path includes and correctly captures the userId parameter (e.g. /:source/user/:userId).
  2. Validate userId is a non-empty string before calling buildApiUrl and return a clear InvalidParameterError if missing.
  3. If the route legitimately allows a no-userId mode for a new source, add that source to the early-return branches alongside 'posts'.
  4. Unit-test buildApiUrl with all source values and undefined userId to confirm only intended sources bypass the check.

Example fix

// before
function buildApiUrl(source: string, userId?: string, contentType?: string): string {
    if (source === 'posts') return `${KEMONO_API_URL}/posts`;
    if (source === 'discord' && userId) return `${KEMONO_API_URL}/discord/channel/lookup/${userId}`;
    if (!userId) throw new Error('User ID is required for non-posts sources');
    ...
}

// after — validate at the route boundary with a typed error
if (source !== 'posts' && source !== 'discord' && !userId) {
    throw new InvalidParameterError(`Source '${source}' requires a user ID. Usage: /kemono/${source}/user/:userId`);
}
Defensive patterns

Strategy: validation

Validate before calling

function assertUserIdForSource(source: string, userId?: string): asserts userId is string {
    if (source !== 'posts' && source !== 'discord' && !userId) {
        throw new InvalidParameterError(`Source '${source}' requires a userId. Route: /kemono/${source}/user/:userId`);
    }
}
// call before buildApiUrl:
assertUserIdForSource(source, userId);

Type guard

function isUserScopedSource(source: string): boolean {
    return source !== 'posts';
}
function hasUserId(source: string, userId?: string): userId is string {
    return source === 'posts' || typeof userId === 'string' && userId.length > 0;
}

Prevention

When it happens

Trigger: The route's path/capturing logic failed to extract the userId path parameter, or the route was called with a source like 'patreon' or 'fanbox' but the userId segment was empty. Also fires if upstream route matching regex changed and the userId capture group no longer binds.

Common situations: A refactor of the route path regex drops the userId capture. A user visits a URL like /kemono/patreon (missing the /user/123 segment) due to a broken link or typo. The parameter is present but empty string (falsy), which also triggers the guard.

Related errors


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