{"record":{"id":"233db7b573db1a1f","repo":"jackwener/OpenCLI","slug":"xiaoyuzhou-history-returned-a-missing-label","errorCode":null,"errorMessage":"Xiaoyuzhou history returned a missing ${label}","messagePattern":"Xiaoyuzhou history returned a missing (.+?)","errorType":"exception","errorClass":"CommandExecutionError","httpStatus":null,"severity":"error","filePath":"clis/xiaoyuzhou/history.js","lineNumber":49,"sourceCode":"\nfunction requiredString(value, label) {\n    if (typeof value !== 'string' || !value.trim()) {\n        throw new CommandExecutionError(`Xiaoyuzhou history returned an invalid ${label}`);\n    }\n    return value.trim();\n}\n\nfunction optionalSeconds(value, label, { positive = false } = {}) {\n    if (value === null) return null;\n    if (!Number.isSafeInteger(value) || value < (positive ? 1 : 0)) {\n        throw new CommandExecutionError(`Xiaoyuzhou history returned an invalid ${label}; expected seconds`);\n    }\n    return value;\n}\n\nfunction optionalIsoTime(value, label, { required = false } = {}) {\n    if (value === null) {\n        if (required) throw new CommandExecutionError(`Xiaoyuzhou history returned a missing ${label}`);\n        return null;\n    }\n    if (typeof value !== 'string' || !value.includes('T') || !Number.isFinite(Date.parse(value))) {\n        throw new CommandExecutionError(`Xiaoyuzhou history returned an invalid ${label}`);\n    }\n    return new Date(value).toISOString();\n}\n\nfunction parseHistoryPage(response) {\n    if (!isRecord(response) || !isRecord(response.raw) || response.raw.data !== response.data) {\n        throw new CommandExecutionError('Xiaoyuzhou history returned an unexpected response shape');\n    }\n    const payload = response?.data;\n    let entries;\n    let next;\n    if (Array.isArray(payload)) {\n        entries = payload;\n        next = response.raw?.loadMoreKey;","sourceCodeStart":31,"sourceCodeEnd":67,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/xiaoyuzhou/history.js#L31-L67","documentation":"This CommandExecutionError is thrown by optionalIsoTime when a required ISO-8601 timestamp (pubDate, with required: true) is absent (null/undefined). Distinguishing 'missing' from 'malformed', this specific message fires only when the value is null even though the library requires it; a present-but-unparseable value raises the sibling 'invalid' error instead.","triggerScenarios":"parseHistoryEpisode calls optionalIsoTime(episode.pubDate, ..., { required: true }); the error fires when episode.pubDate is null or undefined. optionalIsoTime is also called on row.playedAt in parseProgressRows but without required:true, so only pubDate triggers this exact message.","commonSituations":"API schema change renaming pubDate or moving it to a nested object; newly published episodes without a publish date yet in history; cached/proxied responses stripping fields; drafts or scheduled episodes leaking into history without a pubDate.","solutions":["Update the library/CLI in case the field was renamed (e.g. pubDate -> publishedAt) and a newer version reads the new key.","Inspect the raw episode object to confirm whether the field moved or is genuinely absent.","Skip/sanitize rows missing pubDate before parsing if you only need their titles, as a stopgap patch.","Retry with fresh authentication or after a delay — transient partial responses can omit fields.","Report the payload shape to maintainers with the offending episode id."],"exampleFix":"// before\n{ \"episode\": { \"eid\": \"...\", \"pubDate\": null } }\n// after (stopgap sanitize before parse)\nif (entry.episode.pubDate == null) return null; // skip rows without pubDate","handlingStrategy":"validation","validationCode":"function hasPubDate(entry) { return typeof entry?.episode?.pubDate === 'string' && entry.episode.pubDate.includes('T') && !Number.isNaN(Date.parse(entry.episode.pubDate)); }\nconst parseable = entries.filter(hasPubDate); // drop rows without a required pubDate","typeGuard":"const isIsoTimestamp = (v) => typeof v === 'string' && v.includes('T') && Number.isFinite(Date.parse(v));","tryCatchPattern":"try { const rows = await fetchHistory(); } catch (e) { if (e instanceof CommandExecutionError && e.message.includes('missing pubDate')) { console.error('Episode without pubDate — check for renamed field or unpublished episode'); } else throw e; }","preventionTips":["Pre-screen history rows for pubDate presence before parsing.","Keep the CLI updated in case pubDate was renamed upstream.","Validate ISO timestamps (must contain 'T' and parse as a Date) before use.","Handle scheduled/unpublished episodes as a known source of missing pubDate."],"tags":["api-response","schema-validation","missing-field"],"backgroundTag":"missing-required-field","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}