jackwener/OpenCLI · error · CommandExecutionError

Malformed json3 timedtext response: missing events array

Error message

Malformed json3 timedtext response: missing events array

What it means

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.

Source

Thrown at clis/youtube/transcript.js:45

        return null;
    if (Array.isArray(payload))
        return payload;
    if (payload && typeof payload === 'object' && payload.error) {
        throw new CommandExecutionError(String(payload.error));
    }
    throw new CommandExecutionError(`Malformed ${source} payload`);
}

function parseJson3Segments(text) {
    let data;
    try {
        data = JSON.parse(text);
    }
    catch (err) {
        throw new CommandExecutionError(`Malformed json3 timedtext response: ${err?.message || err}`);
    }
    if (!Array.isArray(data?.events)) {
        throw new CommandExecutionError('Malformed json3 timedtext response: missing events array');
    }
    const rows = [];
    for (const event of data.events) {
        const startMs = Number(event?.tStartMs || 0);
        const durMs = Number(event?.dDurationMs || 0);
        const segs = Array.isArray(event?.segs) ? event.segs : [];
        const line = segs.map(seg => seg?.utf8 || '').join('').replace(/\s+/g, ' ').trim();
        if (!line)
            continue;
        rows.push({
            start: startMs / 1000,
            end: (startMs + durMs) / 1000,
            text: line,
        });
    }
    return rows;
}

View on GitHub (pinned to 49907e53dc)

Solutions

  1. Verify the timedtext URL includes fmt=json3 before parsing its body.
  2. Inspect the parsed JSON to confirm what shape YouTube actually returned.
  3. Update the CLI if YouTube changed the json3 schema.
  4. Prefer capture entries explicitly filtered to /api/timedtext?fmt=json3&pot=... (the CLI already does this — check the filter is intact in your version).

Example fix

// before
const segments = parseJson3Segments(body);
// after
const data = JSON.parse(body);
if (!data || !Array.isArray(data.events)) {
  console.error('unexpected timedtext shape, keys:', Object.keys(data || {}));
}
const segments = parseJson3Segments(body);
Defensive patterns

Strategy: validation

Validate before calling

const data = JSON.parse(text);
if (!data || !Array.isArray(data.events)) {
  throw new Error('not a json3 timedtext body: ' + Object.keys(data || {}).join(','));
}

Type guard

function isJson3TimedText(d) {
  return !!d && typeof d === 'object' && Array.isArray(d.events);
}

Try / catch

try {
  const segments = parseJson3Segments(body);
} catch (err) {
  if (err.message.includes('missing events array')) {
    console.error('JSON parsed but wrong schema — check fmt=json3 in the timedtext URL');
  }
}

Prevention

When it happens

Trigger: `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.

Common situations: 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.

Understand the failure class

Background: Schema validation failed / invalid input schema: payload rejected because its shape doesn't match the expected schema — this error's family across 28 libraries.

Related errors


AI-assisted analysis of jackwener/OpenCLI@49907e53dc (2026-08-29). Data as JSON: /api/errors/c9eda2629819c71f. Report an issue: GitHub.