{"record":{"id":"816486053c31d4ef","repo":"jackwener/OpenCLI","slug":"items-error-items-text-items","errorCode":null,"errorMessage":"字幕获取失败: ${items.error}${items.text ? ' — ' + items.text : ''}","messagePattern":"字幕获取失败: (.+?)(.+?)","errorType":"exception","errorClass":"CommandExecutionError","httpStatus":null,"severity":"error","filePath":"clis/bilibili/subtitle.js","lineNumber":114,"sourceCode":"             const subJson = JSON.parse(text);\n             // B站真实返回格式是 { font_size: 0.4, font_color: \"#FFFFFF\", background_alpha: 0.5, background_color: \"#9C27B0\", Stroke: \"none\", type: \"json\" , body: [{from: 0, to: 0, content: \"\"}] }\n             if (Array.isArray(subJson?.body)) return { success: true, data: subJson.body };\n             if (Array.isArray(subJson)) return { success: true, data: subJson };\n             return { error: 'UNKNOWN_JSON', data: subJson };\n         } catch (e) {\n             return { error: 'PARSE_FAILED', text: text.substring(0, 100) };\n         }\n      })()\n    `;\n        let items;\n        try {\n            items = await page.evaluate(fetchJs);\n        }\n        catch (err) {\n            throw new CommandExecutionError(`字幕获取失败: ${err?.message || err}`);\n        }\n        if (items?.error) {\n            throw new CommandExecutionError(`字幕获取失败: ${items.error}${items.text ? ' — ' + items.text : ''}`);\n        }\n        if (!items || typeof items !== 'object' || items.success !== true) {\n            throw new CommandExecutionError('字幕获取结果对象不符合预期格式');\n        }\n        const finalItems = items.data;\n        if (!Array.isArray(finalItems)) {\n            throw new CommandExecutionError('解析到的字幕列表对象不符合数组格式');\n        }\n        if (finalItems.length === 0) {\n            throw new EmptyResultError('bilibili subtitle', '字幕文件中没有字幕片段。');\n        }\n        // 5. 数据映射\n        return finalItems.map((item, idx) => {\n            const from = Number(item?.from);\n            const to = Number(item?.to);\n            if (!item || typeof item !== 'object' || !Number.isFinite(from) || !Number.isFinite(to)) {\n                throw new CommandExecutionError('字幕片段缺少有效 from/to 时间戳');\n            }","sourceCodeStart":96,"sourceCodeEnd":132,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/bilibili/subtitle.js#L96-L132","documentation":"A CommandExecutionError thrown when the in-page fetch script completes but reports an error via its result object (items.error), e.g. the CDN returned a non-OK HTTP status or the JSON body could not be parsed. The error text and optional accompanying text from the page script are included in the message.","triggerScenarios":"The fetchJs inside page.evaluate catches res not ok (e.g. 403/404 from the CDN because the signed subtitle_url expired or was region-blocked) or JSON.parse of the body throws.","commonSituations":"Stale/expired subtitle_url tokens; CDN 403 due to missing Referer handling; subtitle file removed from CDN; region-locked CDN edge nodes.","solutions":["Re-run to obtain a fresh subtitle_url and retry immediately.","Check the embedded error text (after ' — ') for the HTTP status and act on it (403 → referer/auth issue, 404 → file gone).","Retry from a different network/region if the CDN blocks your IP.","Report/patch if Bilibili changed the subtitle JSON format causing parse errors."],"exampleFix":null,"handlingStrategy":"retry","validationCode":"// If you control the fetch, check the response before parsing:\nconst res = await fetch(subUrl);\nif (!res.ok) throw new Error(`CDN ${res.status}`);","typeGuard":null,"tryCatchPattern":"try {\n  await run('bilibili subtitle', { url });\n} catch (e) {\n  if (/字幕获取失败.*403|403/.test(e.message)) throw new Error('Subtitle URL expired or referer-blocked; refresh and retry.');\n  if (/字幕获取失败/.test(e.message)) { await sleep(5000); return run('bilibili subtitle', { url }); }\n  throw e;\n}","preventionTips":["Fetch the subtitle file immediately after obtaining subtitle_url.","Watch the HTTP status embedded after ' — ' in the message.","Retry from another region/IP if CDN blocks yours.","Keep the library current for subtitle JSON format changes."],"tags":["bilibili","subtitle","http-error","cdn","expired-url"],"backgroundTag":"http-403-forbidden","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}