{"record":{"id":"c9eda2629819c71f","repo":"jackwener/OpenCLI","slug":"malformed-json3-timedtext-response-missing-events","errorCode":null,"errorMessage":"Malformed json3 timedtext response: missing events array","messagePattern":"Malformed json3 timedtext response: missing events array","errorType":"exception","errorClass":"CommandExecutionError","httpStatus":null,"severity":"error","filePath":"clis/youtube/transcript.js","lineNumber":45,"sourceCode":"        return null;\n    if (Array.isArray(payload))\n        return payload;\n    if (payload && typeof payload === 'object' && payload.error) {\n        throw new CommandExecutionError(String(payload.error));\n    }\n    throw new CommandExecutionError(`Malformed ${source} payload`);\n}\n\nfunction parseJson3Segments(text) {\n    let data;\n    try {\n        data = JSON.parse(text);\n    }\n    catch (err) {\n        throw new CommandExecutionError(`Malformed json3 timedtext response: ${err?.message || err}`);\n    }\n    if (!Array.isArray(data?.events)) {\n        throw new CommandExecutionError('Malformed json3 timedtext response: missing events array');\n    }\n    const rows = [];\n    for (const event of data.events) {\n        const startMs = Number(event?.tStartMs || 0);\n        const durMs = Number(event?.dDurationMs || 0);\n        const segs = Array.isArray(event?.segs) ? event.segs : [];\n        const line = segs.map(seg => seg?.utf8 || '').join('').replace(/\\s+/g, ' ').trim();\n        if (!line)\n            continue;\n        rows.push({\n            start: startMs / 1000,\n            end: (startMs + durMs) / 1000,\n            text: line,\n        });\n    }\n    return rows;\n}\n","sourceCodeStart":27,"sourceCodeEnd":63,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/youtube/transcript.js#L27-L63","documentation":"After JSON.parse succeeds, parseJson3Segments requires the parsed object to contain an `events` array — the core of the json3 timedtext schema. If `data.events` is missing or not an array, the body is JSON but not a json3 caption track, so this error is thrown with a fixed message.","triggerScenarios":"`parsed` -> `parseJson3Segments` on JSON that parses but lacks `events` — e.g. YouTube returned {\"rc\": true} style status objects, or a different fmt (srv1/srv3/vtt XML) was captured while the code expected fmt=json3.","commonSituations":"Wrong timedtext format fetched (URL missing fmt=json3); YouTube A/B tests changing the json3 schema; capturing a response from a non-caption endpoint that happens to parse as JSON.","solutions":["Verify the timedtext URL includes fmt=json3 before parsing its body.","Inspect the parsed JSON to confirm what shape YouTube actually returned.","Update the CLI if YouTube changed the json3 schema.","Prefer capture entries explicitly filtered to /api/timedtext?fmt=json3&pot=... (the CLI already does this — check the filter is intact in your version)."],"exampleFix":"// before\nconst segments = parseJson3Segments(body);\n// after\nconst data = JSON.parse(body);\nif (!data || !Array.isArray(data.events)) {\n  console.error('unexpected timedtext shape, keys:', Object.keys(data || {}));\n}\nconst segments = parseJson3Segments(body);","handlingStrategy":"validation","validationCode":"const data = JSON.parse(text);\nif (!data || !Array.isArray(data.events)) {\n  throw new Error('not a json3 timedtext body: ' + Object.keys(data || {}).join(','));\n}","typeGuard":"function isJson3TimedText(d) {\n  return !!d && typeof d === 'object' && Array.isArray(d.events);\n}","tryCatchPattern":"try {\n  const segments = parseJson3Segments(body);\n} catch (err) {\n  if (err.message.includes('missing events array')) {\n    console.error('JSON parsed but wrong schema — check fmt=json3 in the timedtext URL');\n  }\n}","preventionTips":["Ensure timedtext URLs include fmt=json3 before parsing","Prefer capture entries explicitly filtered to /api/timedtext with fmt=json3","Log parsed top-level keys when schema errors occur","Update the parser if YouTube alters the json3 schema"],"tags":["youtube","schema-validation","timedtext","malformed-payload"],"backgroundTag":"schema-validation-failed","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}