{"record":{"id":"e8694fdaa526b97c","repo":"jackwener/OpenCLI","slug":"p-p-pagenum-total","errorCode":null,"errorMessage":"分P 序号超出范围：p=${pageNum}（该视频共 ${total} 集）","messagePattern":"分P 序号超出范围：p=(.+?)（该视频共 (.+?) 集）","errorType":"exception","errorClass":"CommandExecutionError","httpStatus":null,"severity":"error","filePath":"clis/bilibili/utils.js","lineNumber":137,"sourceCode":"        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\nconst MIXIN_KEY_ENC_TAB = [\n    46, 47, 18, 2, 53, 8, 23, 32, 15, 50, 10, 31, 58, 3, 45, 35, 27, 43, 5, 49,\n    33, 9, 42, 19, 29, 28, 14, 39, 12, 38, 41, 13, 37, 48, 7, 16, 24, 55, 40,\n    61, 26, 17, 0, 1, 60, 51, 30, 4, 22, 25, 54, 21, 56, 59, 6, 63, 57, 62, 11,\n    36, 20, 34, 44, 52,\n];\nexport function stripHtml(s) {\n    return s.replace(/<[^>]+>/g, '').replace(/&[a-z]+;/gi, ' ').trim();\n}\nexport function payloadData(payload) {\n    return payload?.data ?? payload;\n}\nasync function getNavData(page) {","sourceCodeStart":119,"sourceCodeEnd":155,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/bilibili/utils.js#L119-L155","documentation":"No entry in pages[] matched the requested 分P number, so the requested part does not exist for this video. The error reports the requested p=N and the total number of parts (pages length, falling back to viewData.videos, then 1) so the caller can pick a valid range.","triggerScenarios":"Calling selectVideoPart(viewData, pageNum) (or passing --page N) where N is greater than the number of pages, less than 1, or the video's pages[] simply has no entry with that page number.","commonSituations":"Hardcoding --page 2 for a single-part video; the video was re-uploaded/edited and now has fewer parts; off-by-one confusion (分P numbers are 1-based); a playlist index reused across different videos.","solutions":["Use a 分P number between 1 and the total number of parts shown in the error message (1-based).","Re-fetch the view API and print pages[].page to see valid part numbers before selecting.","Remove the --page flag to download the default (first) part.","If the video changed, update your saved page index — part numbering can shift after edits."],"exampleFix":"// before\nawait cmd(['bili', 'download', url, '--page', '5']); // video only has 3 parts\n// after\nconst view = await getViewData(url);\nconst total = view.pages?.length ?? 1;\nconst page = Math.min(5, total);\nawait cmd(['bili', 'download', url, '--page', String(page)]);","handlingStrategy":"validation","validationCode":"const total = Array.isArray(viewData?.pages) ? viewData.pages.length : 1;\nif (!Number.isInteger(pageNum) || pageNum < 1 || pageNum > total) throw new RangeError(`--page must be 1..${total} (1-based)`);","typeGuard":null,"tryCatchPattern":"try { const part = selectVideoPart(viewData, pageNum); } catch (e) { if (/序号超出范围/.test(e.message)) { console.error(e.message); /* parse total from message and clamp */ } else throw e; }","preventionTips":["Remember 分P numbering is 1-based; clamp page numbers to pages.length.","Re-fetch view data if the video may have been edited (part counts change).","Omit --page for single-part videos.","List pages[].page/part titles to users so they pick valid numbers."],"tags":["api","validation","bilibili","out-of-range","pagination"],"backgroundTag":"invalid-pagination-index","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}