{"record":{"id":"4674709970c60d31","repo":"DIYgod/RSSHub","slug":"invalid-api-response-json-stringify-response","errorCode":null,"errorMessage":"Invalid API response: ${JSON.stringify(response)}","messagePattern":"Invalid API response: (.+?)","errorType":"exception","errorClass":"Error","httpStatus":503,"severity":"error","filePath":"lib/routes/bestblogs/feeds.ts","lineNumber":88,"sourceCode":"\n    const apiRequest = new APIRequest({\n        category,\n        pageSize: defaultPageSize,\n        qualifiedFilter: category === 'featured' ? 'true' : 'false',\n        timeFilter: defaultTimeFilter,\n    });\n\n    const apiUrl = 'https://api.bestblogs.dev/api/resource/list';\n    const response = await ofetch(apiUrl, {\n        headers: {\n            'Content-Type': 'application/json',\n        },\n        method: 'POST',\n        body: apiRequest.toJson(),\n    });\n\n    if (!response || !response.data || !response.data.dataList) {\n        throw new Error('Invalid API response: ' + JSON.stringify(response));\n    }\n\n    const articles = response.data.dataList;\n\n    const items = articles.map((article) => ({\n        title: article.title,\n        link: article.url,\n        description: article.summary,\n        pubDate: parseDate(article.publishDateTimeStr),\n        author: Array.isArray(article.authors) ? article.authors.map((author) => ({ name: author })) : [{ name: article.authors }],\n        category: article.category,\n    }));\n\n    return {\n        title: 'Bestblogs.dev',\n        link: 'https://www.bestblogs.dev/feeds',\n        item: items,\n    };","sourceCodeStart":70,"sourceCodeEnd":106,"githubUrl":"https://github.com/DIYgod/RSSHub/blob/bed535e0879dc71c5aff6f1e7bd1ac21ede40115/lib/routes/bestblogs/feeds.ts#L70-L106","documentation":"Thrown by the bestblogs feeds route after POSTing to the bestblogs.dev resource/list API when the response does not match the expected shape { data: { dataList: [...] } }. The whole response is JSON-stringified into the message so the malformed payload is visible in the error.","triggerScenarios":"The bestblogs API returns an error envelope, an empty body, a shape change (e.g. dataList renamed), a non-JSON page, or a CDN/edge error (HTML 502 page parsed into an object).","commonSituations":"Bestblogs ships a new API version with a different response schema; rate-limiting returns an error object; temporary outage returns HTML that ofetch parsed to null.","solutions":["Retry shortly to rule out a transient outage; if it persists, inspect the JSON in the error message to see what the API actually returned.","If the API schema changed, update the response.data.dataList access path (and the article field mappings) to match the new contract.","Consider validating with a schema (e.g. zod) and throwing a narrower error that distinguishes 'empty' from 'wrong shape'."],"exampleFix":"// before\nif (!response || !response.data || !response.data.dataList) {\n    throw new Error('Invalid API response: ' + JSON.stringify(response));\n}\n\n// after\nconst articles = response?.data?.dataList;\nif (!Array.isArray(articles)) {\n    throw new Error(`bestblogs API returned unexpected shape (code=${response?.code}, msg=${response?.message}). Raw: ${JSON.stringify(response).slice(0, 500)}`);\n}","handlingStrategy":"type-guard","validationCode":"const articles = response?.data?.dataList;\nif (!Array.isArray(articles)) {\n    throw new Error(`bestblogs API unexpected shape: ${JSON.stringify(response).slice(0, 500)}`);\n}","typeGuard":"function isBestblogsResponse(r: unknown): r is { data: { dataList: unknown[] } } {\n    return !!r && typeof r === 'object' && Array.isArray((r as any)?.data?.dataList);\n}","tryCatchPattern":"try {\n    const response = await ofetch(apiUrl, { method: 'POST', body, headers });\n    if (!isBestblogsResponse(response)) throw new Error('Invalid API response');\n} catch (e) {\n    // retry once for transient upstream errors, then propagate\n    throw e;\n}","preventionTips":["Validate upstream JSON with a schema (zod) so shape changes fail loudly with context.","Truncate the JSON in error messages to avoid log bloat.","Distinguish 'empty result' from 'wrong shape' for clearer diagnostics."],"tags":["bestblogs","api-contract","response-validation","upstream"],"backgroundTag":null,"analyzedSha":"bed535e0879dc71c5aff6f1e7bd1ac21ede40115","analyzedAt":"2026-08-12T19:29:35.364Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}