{"record":{"id":"889127fc08b51fa0","repo":"DIYgod/RSSHub","slug":"failed-to-parse-bloglist-from-rsc-data","errorCode":null,"errorMessage":"Failed to parse blogList from RSC data","messagePattern":"Failed to parse blogList from RSC data","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"lib/routes/manus/blog.ts","lineNumber":69,"sourceCode":"    const lines = renderData.split('\\n');\n    for (const line of lines) {\n        if (!line.includes('{\"blogList\":{\"$typeName\"')) {\n            continue;\n        }\n\n        const jsonStr = line.slice(Math.max(0, line.indexOf('{\"blogList\":{\"$typeName\"')));\n        const lastBrace = jsonStr.lastIndexOf('}');\n        try {\n            const parsed = JSON.parse(jsonStr.slice(0, Math.max(0, lastBrace + 1)));\n            blogList = parsed.blogList;\n            break;\n        } catch {\n            // Ignore parse errors and try next line if any\n        }\n    }\n\n    if (!blogList || !blogList.groups) {\n        throw new Error('Failed to parse blogList from RSC data');\n    }\n\n    const list: Array<DataItem & { _contentUrl?: string }> = blogList.groups.flatMap(\n        (group) =>\n            group.blogs?.map((blog) => ({\n                title: blog.title,\n                link: `https://manus.im/blog/${blog.recordUid}`,\n                pubDate: new Date(blog.createdAt.seconds * 1000),\n                description: blog.desc,\n                category: [group.kindName],\n                _contentUrl: blog.contentUrl,\n            })) ?? []\n    );\n\n    const items: DataItem[] = await Promise.all(\n        list.map(\n            (item) =>\n                cache.tryGet(String(item.link), async () => {","sourceCodeStart":51,"sourceCodeEnd":87,"githubUrl":"https://github.com/DIYgod/RSSHub/blob/bed535e0879dc71c5aff6f1e7bd1ac21ede40115/lib/routes/manus/blog.ts#L51-L87","documentation":"Thrown when the Manus blog handler cannot extract a blogList object from the React Server Components (RSC) payload returned by https://manus.im/blog with header RSC:1. The parser scans line-by-line for the marker substring '{\"blogList\":{\"$typeName\"', slices to the last brace, JSON.parses, and gives up if no usable blogList.groups is found.","triggerScenarios":"ofetch returns an RSC payload that no longer contains the '{\"blogList\":{\"$typeName\"' marker, the slice/JSON.parse never succeeds for any line, or blogList exists but has no groups field. Caused by Manus changing their RSC payload shape, returning a non-RSC error page (Cloudflare challenge, 403, maintenance), or an empty blog.","commonSituations":"Manus shipped a frontend change renaming/restructuring blogList; CDN/Cloudflare blocked the RSSHub IP and returned an HTML challenge instead of RSC; transient empty state; the lastBrace slicing logic mis-truncating when the JSON spans differently.","solutions":["Reproduce the request: curl -H 'RSC: 1' https://manus.im/blog and grep for blogList to confirm the marker still exists.","If Manus renamed the key, update the marker substring in blog.ts (lines 53 and 57) to the new shape.","If Cloudflare blocked the request, route through a different IP / set a trueUA / use config.trueUA.","Log renderData length and the matched line count to diagnose silent failures."],"exampleFix":"// before\nif (!blogList || !blogList.groups) {\n    throw new Error('Failed to parse blogList from RSC data');\n}\n// after\nif (!blogList || !blogList.groups) {\n    const hint = renderData.length < 200 ? 'response too short (possibly a challenge page)' : 'blogList marker not found';\n    throw new Error(`Failed to parse blogList from RSC data: ${hint}`);\n}","handlingStrategy":"try-catch","validationCode":"const MARKER = '{\"blogList\":{\"$typeName\"';\nif (!renderData.includes(MARKER)) {\n    // Manus likely changed the RSC shape or returned a challenge page\n    throw new Error('manus.im RSC payload missing blogList marker');\n}","typeGuard":"interface ManusBlogList { groups: Array<{ blogs?: unknown[]; kindName: string }>; }\nconst isBlogList = (v: unknown): v is ManusBlogList =>\n    typeof v === 'object' && v !== null && Array.isArray((v as any).groups);","tryCatchPattern":"try {\n    blogList = parseBlogList(renderData);\n} catch (e) {\n    // fall back to a stable 'feed temporarily unavailable' instead of crashing the route\n    return { title: 'Manus Blog', item: [] };\n}","preventionTips":["Pin a trueUA on the ofetch call so Cloudflare does not return a challenge page.","Search for the marker substring before parsing to fail fast with a clear message.","Subscribe to Manus release notes; RSC payloads are unstable by nature."],"tags":["manus","scraping","rsc","parsing","fragile"],"backgroundTag":null,"analyzedSha":"bed535e0879dc71c5aff6f1e7bd1ac21ede40115","analyzedAt":"2026-08-12T19:29:35.364Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}