jackwener/OpenCLI · error · CommandExecutionError

Xiaoyuzhou history returned an invalid ${label}

Error message

Xiaoyuzhou history returned an invalid ${label}

What it means

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.

Source

Thrown at clis/xiaoyuzhou/history.js:27

const HISTORY_ENDPOINT = '/v1/episode-played/list-history';
const PROGRESS_ENDPOINT = '/v1/playback-progress/list';
const XIAOYUZHOU_ID = /^[0-9a-f]{24}$/i;

function isRecord(value) {
    return value !== null && typeof value === 'object' && !Array.isArray(value);
}

function positiveInteger(value, label, maximum) {
    const parsed = Number(value);
    if (!Number.isInteger(parsed) || parsed < 1 || parsed > maximum) {
        throw new ArgumentError(`--${label} must be an integer between 1 and ${maximum}`);
    }
    return parsed;
}

function requiredId(value, label) {
    if (typeof value !== 'string' || !XIAOYUZHOU_ID.test(value)) {
        throw new CommandExecutionError(`Xiaoyuzhou history returned an invalid ${label}`);
    }
    return value.toLowerCase();
}

function requiredString(value, label) {
    if (typeof value !== 'string' || !value.trim()) {
        throw new CommandExecutionError(`Xiaoyuzhou history returned an invalid ${label}`);
    }
    return value.trim();
}

function optionalSeconds(value, label, { positive = false } = {}) {
    if (value === null) return null;
    if (!Number.isSafeInteger(value) || value < (positive ? 1 : 0)) {
        throw new CommandExecutionError(`Xiaoyuzhou history returned an invalid ${label}; expected seconds`);
    }
    return value;
}

View on GitHub (pinned to 49907e53dc)

Solutions

  1. Update the CLI/library to the latest version in case the API's ID format changed and the regex was already adjusted.
  2. 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.
  3. If behind a proxy/interceptor, bypass it and hit api.xiaoyuzhoufm.com directly.
  4. 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.

Example fix

// before (stubbed test data)
{ "episode": { "eid": "abc123", ... } }
// after
{ "episode": { "eid": "5f1a2b3c4d5e6f7081923a4b", ... } } // 24 hex chars
Defensive patterns

Strategy: type-guard

Validate before calling

function looksLikeHexId(v) { return typeof v === 'string' && /^[0-9a-f]{24}$/i.test(v); }
// pre-screen raw rows before parsing
rows = rawRows.filter(r => looksLikeHexId(r?.episode?.eid) && looksLikeHexId(r?.episode?.pid));

Type guard

const isXiaoyuzhouId = (v) => typeof v === 'string' && /^[0-9a-f]{24}$/.test(v);

Try / catch

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; }

Prevention

When it happens

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

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

Related errors


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