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
- Ensure the route path includes and correctly captures the userId parameter (e.g. /:source/user/:userId).
- Validate userId is a non-empty string before calling buildApiUrl and return a clear InvalidParameterError if missing.
- If the route legitimately allows a no-userId mode for a new source, add that source to the early-return branches alongside 'posts'.
- 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
- Validate userId presence at the route boundary, not deep in URL-builder helpers.
- Use TypeScript assertion functions to narrow userId to string after validation.
- Unit-test buildApiUrl with every supported source value, both with and without userId.
- Keep the route path regex explicit about the userId segment to prevent empty captures.
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
- Invalid URL property: ${prop}
- Invalid Engine Value: ${engine}, please check your config.
- Invalid parameter brief. Please check the doc https://docs.r
- Invalid language code
- Invalid subdomain
AI-assisted analysis of DIYgod/RSSHub@bed535e087 (2026-08-12).
Data as JSON: /api/errors/830667e60b2d72fe.
Report an issue: GitHub.