{"record":{"id":"ed2711640ba72c7b","repo":"jackwener/OpenCLI","slug":"xiaoyuzhou-playback-progress-exceeded-duration-for","errorCode":null,"errorMessage":"Xiaoyuzhou playback progress exceeded duration for eid ${eid}","messagePattern":"Xiaoyuzhou playback progress exceeded duration for eid (.+?)","errorType":"exception","errorClass":"CommandExecutionError","httpStatus":null,"severity":"error","filePath":"clis/xiaoyuzhou/history.js","lineNumber":130,"sourceCode":"    for (const [index, row] of response.data.entries()) {\n        if (!isRecord(row)) {\n            throw new CommandExecutionError(`Xiaoyuzhou playback progress row ${index + 1} is malformed`);\n        }\n        const eid = requiredId(row.eid, `progress eid in row ${index + 1}`);\n        const episode = requested.get(eid);\n        if (!episode) {\n            throw new CommandExecutionError(`Xiaoyuzhou playback progress returned unrequested eid ${eid}`);\n        }\n        if (progressById.has(eid)) {\n            throw new CommandExecutionError(`Xiaoyuzhou playback progress returned duplicate eid ${eid}`);\n        }\n        const pid = requiredId(row.pid, `progress pid in row ${index + 1}`);\n        if (pid !== episode.pid) {\n            throw new CommandExecutionError(`Xiaoyuzhou playback progress pid did not match history eid ${eid}`);\n        }\n        const progressSec = optionalSeconds(row.progress, `progress in row ${index + 1}`);\n        if (progressSec !== null && episode.durationSec !== null && progressSec > episode.durationSec) {\n            throw new CommandExecutionError(`Xiaoyuzhou playback progress exceeded duration for eid ${eid}`);\n        }\n        progressById.set(eid, {\n            progressSec,\n            playedAt: optionalIsoTime(row.playedAt, `playedAt in row ${index + 1}`),\n        });\n    }\n    for (const episode of episodes) {\n        if (!progressById.has(episode.eid)) {\n            throw new CommandExecutionError(\n                `Xiaoyuzhou playback progress omitted requested eid ${episode.eid}; the history join is incomplete`,\n            );\n        }\n    }\n    return progressById;\n}\n\nasync function fetchHistory(args = {}) {\n    const fetchAll = args.all ?? false;","sourceCodeStart":112,"sourceCodeEnd":148,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/xiaoyuzhou/history.js#L112-L148","documentation":"parseProgressRows sanity-checks that a row's playback position never exceeds the episode duration learned from history. This error means the reported progress in seconds is larger than the episode's duration, indicating corrupted, mis-joined, or wrong-unit data.","triggerScenarios":"A progress row has progress greater than the matching episode's durationSec — e.g. progress reported in milliseconds while duration is in seconds, a missing/zero duration from history, or the server returning garbage progress values.","commonSituations":"Upstream switches progress to milliseconds; history entries with missing durations paired with large progress values; fixtures with inconsistent units; bugs producing inflated progress values.","solutions":["Inspect row.progress and episode.durationSec units; convert progress to seconds (e.g. divide by 1000) if the API switched units.","Re-fetch both history and progress so durations and progress come from the same snapshot.","Clamp or filter progress values that exceed duration before parsing if you control the data source.","Report the anomalous response to the API provider if live data is genuinely inconsistent."],"exampleFix":"// before\nconst progressSec = optionalSeconds(row.progress, `progress in row ${index + 1}`);\n// after (API changed to milliseconds)\nconst rawProgress = optionalSeconds(row.progress, `progress in row ${index + 1}`);\nconst progressSec = rawProgress === null ? null : Math.min(Math.round(rawProgress / 1000), episode.durationSec ?? Infinity);","handlingStrategy":"validation","validationCode":"for (const r of progressRows) {\n  const ep = episodesById.get(r.eid);\n  if (ep && ep.durationSec !== null && r.progress > ep.durationSec) {\n    throw new Error(`progress ${r.progress} exceeds duration ${ep.durationSec} for ${r.eid}`);\n  }\n}","typeGuard":null,"tryCatchPattern":"try {\n  const progress = progressById(episodes);\n} catch (e) {\n  if (e instanceof CommandExecutionError && /exceeded duration/.test(e.message)) {\n    console.error('Unit mismatch or bad progress data:', e.message);\n  } else throw e;\n}","preventionTips":["Confirm progress and duration use the same time unit (seconds vs milliseconds).","Clamp or drop physically impossible progress values at the data boundary.","Fetch history and progress from the same snapshot."],"tags":["api","data-integrity","units","validation"],"backgroundTag":"value-out-of-range","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}