DIYgod/RSSHub · error · Error
Failed to fetch data from Kemono: ${error instanceof Error ?
Error message
Failed to fetch data from Kemono: ${error instanceof Error ? error.message : 'Unknown error'} What it means
The top-level catch-all in the Kemono handler. Every error thrown inside the try block (network failures, the buildApiUrl/buildFrontendUrl validation errors, JSON parse errors, non-200 responses from got) is caught and re-wrapped as 'Failed to fetch data from Kemono: <original message>' with the original attached via { cause }. This obscures the original error type and makes upstream handling harder.
Source
Thrown at lib/routes/kemono/index.tsx:418
title = `Announcements of ${authorName} from ${source} | Kemono`;
items = processAnnouncements(response.data, authorName, source, userId, limit);
} else if (contentType === 'fancards') {
title = `Fancards of ${authorName} from ${source} | Kemono`;
items = processFancards(response.data, authorName, source, userId, limit);
} else {
title = isPostsMode ? 'Kemono Posts' : `Posts of ${authorName} from ${source} | Kemono`;
const posts = isPostsMode ? response.data.posts : response.data;
items = processPosts(posts, authorName, limit);
}
return {
title,
image: iconUrl,
link: frontendUrl,
item: items,
};
} catch (error) {
throw new Error(`Failed to fetch data from Kemono: ${error instanceof Error ? error.message : 'Unknown error'}`, { cause: error });
}
}
View on GitHub (pinned to bed535e087)
Solutions
- Do not blanket-wrap — let typed errors (InvalidParameterError, ConfigNotFoundError) propagate unchanged so callers can distinguish bad input from server faults.
- Only wrap unexpected/network errors, and preserve the HTTP status code in the message or error properties.
- Add status-code-specific handling (404 → 'creator not found', 429 → 'rate limited, retry later', 5xx → 'Kemono server error').
- Keep the { cause } chain (good practice) but stop stringifying the inner error which can lose structured info.
Example fix
// before
} catch (error) {
throw new Error(`Failed to fetch data from Kemono: ${error instanceof Error ? error.message : 'Unknown error'}`, { cause: error });
}
// after — let expected errors through, wrap only unexpected ones
} catch (error) {
if (error instanceof InvalidParameterError || error instanceof ConfigNotFoundError) throw error;
const status = (error as any)?.response?.statusCode;
if (status === 404) throw new Error(`Kemono creator/channel not found for source=${source} userId=${userId}`, { cause: error });
if (status === 429) throw new Error('Kemono rate limit reached; retry later', { cause: error });
throw new Error(`Failed to fetch data from Kemono (status ${status ?? 'n/a'}): ${error instanceof Error ? error.message : error}`, { cause: error });
} Defensive patterns
Strategy: try-catch
Type guard
function isHttpError(e: unknown): e is { response?: { statusCode: number }; message: string } {
return typeof e === 'object' && e !== null && 'response' in e;
} Try / catch
} catch (error) {
// Let validation/config errors propagate unchanged
if (error instanceof InvalidParameterError || error instanceof ConfigNotFoundError) throw error;
const status = (error as any)?.response?.statusCode;
if (status === 404) throw new Error(`Kemono resource not found (source=${source}, userId=${userId})`, { cause: error });
if (status === 429) throw new Error('Kemono rate limit; retry later', { cause: error });
throw new Error(`Kemono fetch failed (status ${status ?? 'n/a'}): ${error instanceof Error ? error.message : error}`, { cause: error });
} Prevention
- Do not blanket-wrap all errors — let typed errors (InvalidParameterError, ConfigNotFoundError) propagate.
- Branch on HTTP status code to give actionable messages (404, 429, 5xx).
- Preserve the cause chain ({ cause }) for debugging but avoid stringifying structured errors.
- Add retry for transient (429/5xx/network) failures before surfacing the error.
When it happens
Trigger: Any uncaught error within the handler: Kemono API returns 404 for a nonexistent creator, 5xx during an outage, network timeout, the 'User ID is required' validation from buildApiUrl, a JSON shape change causing processPosts to throw, or the profile fetch failing.
Common situations: Kemono is temporarily down or rate-limiting. A creator deleted their account (404). The API response schema changed and downstream destructuring throws. The catch-all conflates these distinct conditions into one generic message, making monitoring and retries harder.
Related errors
- ${(error as { responseText: string }).responseText}
- something wrong
- 日报数据不存在或为空
- Got error code ${data.code} while fetching: ${data.message}
- Failed to fetch data from API
AI-assisted analysis of DIYgod/RSSHub@bed535e087 (2026-08-12).
Data as JSON: /api/errors/461b07da79a75c94.
Report an issue: GitHub.