{"record":{"id":"7cc39389a399832a","repo":"DIYgod/RSSHub","slug":"http-error-status-res-status-7cc393","errorCode":null,"errorMessage":"HTTP error! status: ${res.status}","messagePattern":"HTTP error! status: (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"lib/routes/fanbox/utils.tsx","lineNumber":189,"sourceCode":"    return ret;\n}\n\nexport function parseItem(page: Page, item: PostItem) {\n    return cache.tryGet(`fanbox-${item.id}-${item.updatedDatetime}`, async () => {\n        const postDetail: PostDetailResponse = await page.evaluate(\n            async ({ url }) => {\n                const res = await fetch(url, {\n                    method: 'GET',\n                    credentials: 'include',\n                    headers: {\n                        'Sec-Fetch-Dest': 'empty',\n                        'Sec-Fetch-Mode': 'cors',\n                        'Sec-Fetch-Site': 'same-site',\n                    },\n                });\n\n                if (!res.ok) {\n                    throw new Error(`HTTP error! status: ${res.status}`);\n                }\n\n                return res.json();\n            },\n            { url: `https://api.fanbox.cc/post.info?postId=${item.id}` }\n        );\n\n        return {\n            title: item.title || 'No title',\n            description: await parseDetail(postDetail.body.post),\n            pubDate: parseDate(item.updatedDatetime),\n            link: `https://${item.creatorId}.fanbox.cc/posts/${item.id}`,\n            category: item.tags,\n        };\n    }) as Promise<DataItem>;\n}\n\nasync function getSoundCloudEmbedUrl(videoId: string) {","sourceCodeStart":171,"sourceCodeEnd":207,"githubUrl":"https://github.com/DIYgod/RSSHub/blob/bed535e0879dc71c5aff6f1e7bd1ac21ede40115/lib/routes/fanbox/utils.tsx#L171-L207","documentation":"Unlike other routes, this path uses the native `fetch` (not `got`) to call the Fanbox `post.info` API with credentials included, and throws a generic Error echoing `res.status` on any non-OK response. The status code is the key diagnostic: 401 means the session cookie is missing/invalid, 403/451 is an access restriction, 429 is rate limiting.","triggerScenarios":"Calling post.info without a valid FANBOX_SESSION cookie (401); hitting Fanbox rate limits (429); requesting a deleted/creator-restricted post (404); a transient 5xx.","commonSituations":"Self-hosting without configuring the Fanbox session cookie; the cookie expired since last refresh; aggressive polling tripping rate limits.","solutions":["Set or refresh the FANBOX_SESSION cookie in your RSSHub config and restart.","Read the status from the message: 401 -> auth, 429 -> back off, 5xx -> retry.","For 429/5xx, retry with exponential backoff; for 401, do not retry until credentials are fixed."],"exampleFix":"// before\n//   if (!res.ok) {\n//       throw new Error(`HTTP error! status: ${res.status}`);\n//   }\n// after\n//   if (!res.ok) {\n//       if (res.status === 401) throw new Error('Fanbox session cookie is missing or expired (401)');\n//       if (res.status === 429) throw new Error('Fanbox rate limited (429) - retry later');\n//       throw new Error(`HTTP error! status: ${res.status}`);\n//   }","handlingStrategy":"retry","validationCode":"// Validate credentials presence before calling\nif (!config.fanbox?.session) {\n  throw new Error('FANBOX_SESSION is not configured — expect 401 from post.info');\n}","typeGuard":null,"tryCatchPattern":"async function fetchFanbox(url: string) {\n  for (let attempt = 0; attempt < 3; attempt++) {\n    const res = await fetch(url, { credentials: 'include' });\n    if (res.ok) return res.json();\n    if (res.status === 401 || res.status === 404) throw new Error(`Fanbox ${res.status}`);\n    // 429 / 5xx — back off and retry\n    await new Promise((r) => setTimeout(r, 2 ** attempt * 500));\n  }\n  throw new Error('Fanbox request failed after retries');\n}","preventionTips":["Distinguish 401 (fix creds) from 429/5xx (retry) before giving up.","Refresh the session cookie on a schedule; do not let it expire silently.","Rate-limit outbound Fanbox calls to stay under their threshold."],"tags":["network","http","auth","rate-limit"],"backgroundTag":null,"analyzedSha":"bed535e0879dc71c5aff6f1e7bd1ac21ede40115","analyzedAt":"2026-08-12T19:29:35.364Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}