{"record":{"id":"e0027743bb824614","repo":"jackwener/OpenCLI","slug":"xiaoyuzhou-history-returned-an-invalid-label","errorCode":null,"errorMessage":"Xiaoyuzhou history returned an invalid ${label}","messagePattern":"Xiaoyuzhou history returned an invalid (.+?)","errorType":"exception","errorClass":"CommandExecutionError","httpStatus":null,"severity":"error","filePath":"clis/xiaoyuzhou/history.js","lineNumber":27,"sourceCode":"const HISTORY_ENDPOINT = '/v1/episode-played/list-history';\nconst PROGRESS_ENDPOINT = '/v1/playback-progress/list';\nconst XIAOYUZHOU_ID = /^[0-9a-f]{24}$/i;\n\nfunction isRecord(value) {\n    return value !== null && typeof value === 'object' && !Array.isArray(value);\n}\n\nfunction positiveInteger(value, label, maximum) {\n    const parsed = Number(value);\n    if (!Number.isInteger(parsed) || parsed < 1 || parsed > maximum) {\n        throw new ArgumentError(`--${label} must be an integer between 1 and ${maximum}`);\n    }\n    return parsed;\n}\n\nfunction requiredId(value, label) {\n    if (typeof value !== 'string' || !XIAOYUZHOU_ID.test(value)) {\n        throw new CommandExecutionError(`Xiaoyuzhou history returned an invalid ${label}`);\n    }\n    return value.toLowerCase();\n}\n\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}","sourceCodeStart":9,"sourceCodeEnd":45,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/xiaoyuzhou/history.js#L9-L45","documentation":"This CommandExecutionError is thrown by requiredId when the Xiaoyuzhou history API returns an episode or progress identifier (eid/pid) that is not a 24-character hex string (the /​^[0-9a-f]{24}$/​ pattern). It guards against the upstream API changing its ID format or returning malformed records, so the failure indicates bad/changed server data, not user input.","triggerScenarios":"parseHistoryEpisode calls requiredId on episode.eid or episode.pid, and parseProgressRows calls it on row.eid/row.pid; the error fires whenever one of those values is missing, not a string, or fails the 24-hex-char regex — e.g. the API omits eid, returns a numeric ID, or switches to a new ID format.","commonSituations":"Xiaoyuzhou API schema change (new ID format or field rename) after a library/CLI update; a proxy or cached/intercepted response returning partial episode objects; mock/stub servers in tests returning fake short IDs; region-specific API responses with missing fields.","solutions":["Update the CLI/library to the latest version in case the API's ID format changed and the regex was already adjusted.","Capture the raw response (curl with your auth credentials to /v1/episode-played/list-history or /v1/playback-progress/list) and inspect which row has the malformed eid/pid.","If behind a proxy/interceptor, bypass it and hit api.xiaoyuzhoufm.com directly.","Report the malformed payload to the library maintainers with the offending row; as a stopgap, filter out rows with non-24-hex ids before parsing."],"exampleFix":"// before (stubbed test data)\n{ \"episode\": { \"eid\": \"abc123\", ... } }\n// after\n{ \"episode\": { \"eid\": \"5f1a2b3c4d5e6f7081923a4b\", ... } } // 24 hex chars","handlingStrategy":"type-guard","validationCode":"function looksLikeHexId(v) { return typeof v === 'string' && /^[0-9a-f]{24}$/i.test(v); }\n// pre-screen raw rows before parsing\nrows = rawRows.filter(r => looksLikeHexId(r?.episode?.eid) && looksLikeHexId(r?.episode?.pid));","typeGuard":"const isXiaoyuzhouId = (v) => typeof v === 'string' && /^[0-9a-f]{24}$/.test(v);","tryCatchPattern":"try { const rows = await fetchHistory(); } catch (e) { if (e instanceof CommandExecutionError && e.message.includes('invalid eid') || e.message.includes('invalid pid')) { console.error('Upstream returned malformed IDs — check API schema version'); } else throw e; }","preventionTips":["Pin the CLI/library version matching the current Xiaoyuzhou API schema.","Pre-validate API payloads with a schema lib (zod/ajv) using the 24-hex pattern before parsing.","Log raw responses so malformed rows are diagnosable.","Watch for API changelog announcements about ID format changes."],"tags":["api-response","schema-validation","upstream-data"],"backgroundTag":"invalid-api-response-shape","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}