{"record":{"id":"762b2fab6d57b95d","repo":"jackwener/OpenCLI","slug":"string-payload-error","errorCode":null,"errorMessage":"String(payload.error)","messagePattern":"String\\(payload\\.error\\)","errorType":"exception","errorClass":"CommandExecutionError","httpStatus":null,"severity":"error","filePath":"clis/youtube/transcript.js","lineNumber":31,"sourceCode":"import { extractJsonAssignmentFromHtml, parseVideoId, prepareYoutubeApiPage } from './utils.js';\nimport { groupTranscriptSegments, formatGroupedTranscript, } from './transcript-group.js';\nimport { CommandExecutionError, EmptyResultError } from '@jackwener/opencli/errors';\n\nfunction unwrapBrowserResult(value) {\n    if (value && typeof value === 'object' && 'session' in value && 'data' in value) {\n        return value.data;\n    }\n    return value;\n}\n\nfunction normalizeSegmentsPayload(value, source, { allowNull = false } = {}) {\n    const payload = unwrapBrowserResult(value);\n    if (payload == null && allowNull)\n        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);","sourceCodeStart":13,"sourceCodeEnd":49,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/youtube/transcript.js#L13-L49","documentation":"normalizeSegmentsPayload validates the unwrapped result of a browser-evaluated YouTube player caption extraction. When the browser page returns an object with an `error` field (a structured in-page failure), the CLI rethrows it as a CommandExecutionError so the caller sees the real page-side reason instead of silent null segments. It is a deliberate error-propagation bridge from browser context to CLI context.","triggerScenarios":"Calling `segments` -> `normalizeSegmentsPayload(playerResult, ...)` where the in-page evaluate returned {error: \"...\"} — e.g. the YouTube player response lacked captionTracks or the page script caught its own failure and serialized it as an error field.","commonSituations":"YouTube DOM/player changes breaking the injected caption-extraction script; videos where caption data is gated (age-restricted, consent walls); stale browser tab in a shared daemon session returning a cached error object.","solutions":["Read the thrown message — it is the page-side error text — and fix the underlying in-page extraction cause (often a DOM/player API change).","Update the YouTube CLI to the latest version so the injected player-extraction script matches current YouTube markup.","Verify the browser tab/daemon session is fresh (restart the browser session) to clear stale SPA state.","Check the video actually has captions; if not, handle the EMPTY_RESULT path instead of retrying."],"exampleFix":"// before\nconst segments = normalizeSegmentsPayload(playerResult, 'player caption extraction', { allowNull: true });\n// after\ntry {\n  const segments = normalizeSegmentsPayload(playerResult, 'player caption extraction', { allowNull: true });\n} catch (err) {\n  console.error('player caption extraction failed:', err.message);\n  // fall back to network-capture path\n}","handlingStrategy":"try-catch","validationCode":"if (payload && typeof payload === 'object' && !Array.isArray(payload) && payload.error) {\n  // page reported an error; handle before calling the extractor\n}","typeGuard":"function isSegmentsPayload(v) {\n  return v == null || Array.isArray(v) || (typeof v === 'object' && !('error' in v));\n}","tryCatchPattern":"try {\n  const segments = normalizeSegmentsPayload(playerResult, 'player caption extraction', { allowNull: true });\n} catch (err) {\n  // err.message is the page-side error text; log and fall back to network capture\n}","preventionTips":["Always unwrap and shape-check browser results before consuming them","Keep the CLI updated against YouTube player API changes","Use fresh browser sessions for shared daemon tabs","Confirm the video has captions before extraction"],"tags":["youtube","browser-automation","payload-validation","error-propagation"],"backgroundTag":"browser-payload-error-field","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}