{"record":{"id":"437c272fbd8a3880","repo":"jackwener/OpenCLI","slug":"bilibili-conclusion-api-returned-malformed-outline","errorCode":null,"errorMessage":"Bilibili conclusion API returned malformed outline","messagePattern":"Bilibili conclusion API returned malformed outline","errorType":"exception","errorClass":"CommandExecutionError","httpStatus":null,"severity":"error","filePath":"clis/bilibili/summary.js","lineNumber":96,"sourceCode":"    }\n    let modelResult = data.model_result;\n    if (typeof modelResult === 'string') {\n        try {\n            modelResult = JSON.parse(modelResult);\n        } catch {\n            throw new CommandExecutionError('Bilibili conclusion API returned malformed model_result JSON');\n        }\n    }\n    if (!modelResult || typeof modelResult !== 'object' || Array.isArray(modelResult)) {\n        throw new CommandExecutionError('Bilibili conclusion API returned malformed model_result');\n    }\n    const summary = String(modelResult.summary ?? '').trim();\n    if (!summary) {\n        throw new EmptyResultError('bilibili summary', `Bilibili has not generated an AI summary for ${bvid}.`);\n    }\n    const outline = modelResult.outline ?? [];\n    if (!Array.isArray(outline)) {\n        throw new CommandExecutionError('Bilibili conclusion API returned malformed outline');\n    }\n    return { summary, outline };\n}\n\nfunction rowsFromModel(model) {\n    const rows = [{ time: '', content: model.summary }];\n    for (const section of model.outline) {\n        if (!section || typeof section !== 'object' || Array.isArray(section)) {\n            throw new CommandExecutionError('Bilibili conclusion API returned malformed outline section');\n        }\n        const sectionTitle = String(section.title ?? '').trim();\n        const sectionTime = formatTime(section.timestamp);\n        if (sectionTitle) {\n            rows.push({ time: sectionTime, content: `# ${sectionTitle}` });\n        }\n        const points = section.part_outline ?? [];\n        if (!Array.isArray(points)) {\n            throw new CommandExecutionError('Bilibili conclusion API returned malformed part outline');","sourceCodeStart":78,"sourceCodeEnd":114,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/bilibili/summary.js#L78-L114","documentation":"readModelResult expects model_result.outline to be an array of chapter sections (or absent, defaulting to []). If the field is present but is not an array — e.g. an object or a string — this CommandExecutionError is thrown because the outline cannot be iterated to build the summary table rows.","triggerScenarios":"The conclusion API returned a model_result whose outline is a non-array value (object with named keys, JSON-encoded string, etc.), or the caller passed a hand-built model with a wrong outline type.","commonSituations":"Bilibili changing the outline schema for some videos; middle-layer code that pre-decodes outline into an object; test fixtures with incorrect shapes.","solutions":["Log typeof modelResult.outline and its JSON to see the actual shape.","If outline arrives as an encoded string, JSON.parse it before validation.","Normalize object-shaped outlines via Object.values() before use.","Default to an empty outline (summary-only output) when the shape is unrecognized.","Report/check for recent Bilibili API schema changes."],"exampleFix":"// before\nconst outline = modelResult.outline ?? [];\nif (!Array.isArray(outline)) {\n    throw new CommandExecutionError('Bilibili conclusion API returned malformed outline');\n}\n// after\nlet outline = modelResult.outline ?? [];\nif (typeof outline === 'string') {\n    try { outline = JSON.parse(outline); } catch { outline = []; }\n}\nif (!Array.isArray(outline)) outline = Object.values(outline ?? {});","handlingStrategy":"validation","validationCode":"const raw = modelResult?.outline;\nconst outline = raw == null ? [] : (Array.isArray(raw) ? raw : Object.values(raw));\nif (!Array.isArray(outline)) console.warn('outline not usable');","typeGuard":"function isOutlineArray(v) {\n  return v === undefined || v === null || (Array.isArray(v) && v.every(s => s && typeof s === 'object' && !Array.isArray(s)));\n}","tryCatchPattern":"try {\n  const rows = rowsFromModel(model);\n} catch (e) {\n  if (String(e.message).includes('malformed outline')) {\n    return [{ time: '', content: model.summary }];\n  }\n  throw e;\n}","preventionTips":["JSON.parse outline if the API string-encodes it","Default missing outline to [] and degrade to summary-only output","Validate the whole model shape once after fetching","Keep fixtures matching the real API schema"],"tags":["bilibili","schema-validation","outline","api-response"],"backgroundTag":"schema-validation-failed","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}