{"record":{"id":"eb3222ba0f6ff825","repo":"jackwener/OpenCLI","slug":"bilibili-view-api-returned-a-malformed-pages-ent","errorCode":null,"errorMessage":"Bilibili view API returned a malformed pages[] entry","messagePattern":"Bilibili view API returned a malformed pages\\[\\] entry","errorType":"exception","errorClass":"CommandExecutionError","httpStatus":null,"severity":"error","filePath":"clis/bilibili/utils.js","lineNumber":124,"sourceCode":"        if (Number.isSafeInteger(n)) return n;\n    }\n    throw new CommandExecutionError(`Bilibili view API returned a malformed ${label}`);\n}\n\n/**\n * 从 view API 的 data.pages 数组取第 N 集（1-based）。\n * page/cid 都以 view API 的 pages[] 为 source-of-truth；缺失、重复或畸形都 fail closed。\n * 返回该集 raw 对象（含 cid / part(分集标题) / page / duration）。\n */\nexport function selectVideoPart(viewData, pageNum) {\n    const pages = Array.isArray(viewData?.pages) ? viewData.pages : null;\n    if (!pages || pages.length === 0) {\n        throw new CommandExecutionError('Bilibili view API did not return pages[] for --page selection');\n    }\n    const matches = [];\n    for (const entry of pages) {\n        if (!entry || typeof entry !== 'object' || Array.isArray(entry)) {\n            throw new CommandExecutionError('Bilibili view API returned a malformed pages[] entry');\n        }\n        const apiPage = readApiPositiveInteger(entry.page, 'page number');\n        if (apiPage === pageNum) {\n            matches.push(entry);\n        }\n    }\n    if (matches.length > 1) {\n        throw new CommandExecutionError(`Bilibili view API returned duplicate page entries for p=${pageNum}`);\n    }\n    const part = matches[0];\n    if (!part) {\n        const total = pages.length || viewData?.videos || 1;\n        throw new CommandExecutionError(`分P 序号超出范围：p=${pageNum}（该视频共 ${total} 集）`);\n    }\n    readApiPositiveInteger(part.cid, `cid for p=${pageNum}`);\n    return part;\n}\n","sourceCodeStart":106,"sourceCodeEnd":142,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/bilibili/utils.js#L106-L142","documentation":"Each entry in the view API's pages[] array must be a non-null, non-array object so its page/cid fields can be read. selectVideoPart throws this fail-closed error when any entry is null, a primitive, or an array, since a malformed entry could cause the wrong part to be selected silently.","triggerScenarios":"Iterating viewData.pages and encountering an entry that is null, undefined, a number/string/boolean, or an Array — i.e., Bilibili returned a structurally corrupt pages[] list.","commonSituations":"Bilibili API incidents returning degraded JSON; intermediary proxies mangling the response; schema drift where pages contains unexpected element shapes; hand-written test fixtures with wrong element types.","solutions":["Re-fetch the view API response — corrupted payloads are often transient.","Log the raw response body and inspect the offending pages[] element.","Verify you are calling the official /x/web-interface/view endpoint, not a mirror/proxy that alters JSON.","If the schema changed, update selectVideoPart in clis/bilibili/utils.js to match the new shape."],"exampleFix":"// before\nconst pages = viewData.pages; // may contain null or junk entries\n// after\nconst pages = (viewData?.pages ?? []).filter(\n  (e) => e && typeof e === 'object' && !Array.isArray(e) && typeof e.page === 'number'\n);","handlingStrategy":"type-guard","validationCode":"const cleanPages = (viewData?.pages ?? []).filter(e => e && typeof e==='object' && !Array.isArray(e));\nif (cleanPages.length !== (viewData?.pages ?? []).length) console.warn('corrupt pages[] entries dropped');","typeGuard":"function isPageEntry(e){ return !!e && typeof e==='object' && !Array.isArray(e) && typeof e.page!=='undefined' && typeof e.cid!=='undefined'; }","tryCatchPattern":"try { const part = selectVideoPart(viewData, pageNum); } catch (e) { if (/malformed pages/.test(e.message)) { logRawResponse(viewData); throw new Error('Bilibili returned corrupt pages[]; inspect payload'); } throw e; }","preventionTips":["Filter/validate each pages[] element before part selection.","Log the full raw response when a malformed entry appears to identify the corrupting layer (proxy/CDN/API).","Use recorded-response tests to detect schema drift early.","Avoid third-party mirrors of the view API that may rewrite JSON."],"tags":["api","validation","bilibili","fail-closed"],"backgroundTag":"malformed-api-response","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}