{"record":{"id":"461b07da79a75c94","repo":"DIYgod/RSSHub","slug":"failed-to-fetch-data-from-kemono-error-instance","errorCode":null,"errorMessage":"Failed to fetch data from Kemono: ${error instanceof Error ? error.message : 'Unknown error'}","messagePattern":"Failed to fetch data from Kemono: (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"lib/routes/kemono/index.tsx","lineNumber":418,"sourceCode":"            title = `Announcements of ${authorName} from ${source} | Kemono`;\n            items = processAnnouncements(response.data, authorName, source, userId, limit);\n        } else if (contentType === 'fancards') {\n            title = `Fancards of ${authorName} from ${source} | Kemono`;\n            items = processFancards(response.data, authorName, source, userId, limit);\n        } else {\n            title = isPostsMode ? 'Kemono Posts' : `Posts of ${authorName} from ${source} | Kemono`;\n            const posts = isPostsMode ? response.data.posts : response.data;\n            items = processPosts(posts, authorName, limit);\n        }\n\n        return {\n            title,\n            image: iconUrl,\n            link: frontendUrl,\n            item: items,\n        };\n    } catch (error) {\n        throw new Error(`Failed to fetch data from Kemono: ${error instanceof Error ? error.message : 'Unknown error'}`, { cause: error });\n    }\n}\n","sourceCodeStart":400,"sourceCodeEnd":421,"githubUrl":"https://github.com/DIYgod/RSSHub/blob/bed535e0879dc71c5aff6f1e7bd1ac21ede40115/lib/routes/kemono/index.tsx#L400-L421","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\n} catch (error) {\n    throw new Error(`Failed to fetch data from Kemono: ${error instanceof Error ? error.message : 'Unknown error'}`, { cause: error });\n}\n\n// after — let expected errors through, wrap only unexpected ones\n} catch (error) {\n    if (error instanceof InvalidParameterError || error instanceof ConfigNotFoundError) throw error;\n    const status = (error as any)?.response?.statusCode;\n    if (status === 404) throw new Error(`Kemono creator/channel not found for source=${source} userId=${userId}`, { cause: error });\n    if (status === 429) throw new Error('Kemono rate limit reached; retry later', { cause: error });\n    throw new Error(`Failed to fetch data from Kemono (status ${status ?? 'n/a'}): ${error instanceof Error ? error.message : error}`, { cause: error });\n}","handlingStrategy":"try-catch","validationCode":null,"typeGuard":"function isHttpError(e: unknown): e is { response?: { statusCode: number }; message: string } {\n    return typeof e === 'object' && e !== null && 'response' in e;\n}","tryCatchPattern":"} catch (error) {\n    // Let validation/config errors propagate unchanged\n    if (error instanceof InvalidParameterError || error instanceof ConfigNotFoundError) throw error;\n    const status = (error as any)?.response?.statusCode;\n    if (status === 404) throw new Error(`Kemono resource not found (source=${source}, userId=${userId})`, { cause: error });\n    if (status === 429) throw new Error('Kemono rate limit; retry later', { cause: error });\n    throw new Error(`Kemono fetch failed (status ${status ?? 'n/a'}): ${error instanceof Error ? error.message : error}`, { cause: error });\n}","preventionTips":["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."],"tags":["error-handling","catch-all","kemono","network","observability"],"backgroundTag":null,"analyzedSha":"bed535e0879dc71c5aff6f1e7bd1ac21ede40115","analyzedAt":"2026-08-12T19:29:35.364Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}