jackwener/OpenCLI · error · CommandExecutionError

Xiaoyuzhou playback progress pid did not match history eid $

Error message

Xiaoyuzhou playback progress pid did not match history eid ${eid}

What it means

After resolving each progress row's eid to a requested episode, parseProgressRows verifies the row's pid (podcast id) equals the pid of the matching history episode. A mismatch means the progress row and the history entry describe different podcasts, so the join is inconsistent and the library refuses to produce misleading data.

Source

Thrown at clis/xiaoyuzhou/history.js:126

        throw new CommandExecutionError('Xiaoyuzhou playback progress returned an unexpected response shape');
    }
    const requested = new Map(episodes.map((episode) => [episode.eid, episode]));
    const progressById = new Map();
    for (const [index, row] of response.data.entries()) {
        if (!isRecord(row)) {
            throw new CommandExecutionError(`Xiaoyuzhou playback progress row ${index + 1} is malformed`);
        }
        const eid = requiredId(row.eid, `progress eid in row ${index + 1}`);
        const episode = requested.get(eid);
        if (!episode) {
            throw new CommandExecutionError(`Xiaoyuzhou playback progress returned unrequested eid ${eid}`);
        }
        if (progressById.has(eid)) {
            throw new CommandExecutionError(`Xiaoyuzhou playback progress returned duplicate eid ${eid}`);
        }
        const pid = requiredId(row.pid, `progress pid in row ${index + 1}`);
        if (pid !== episode.pid) {
            throw new CommandExecutionError(`Xiaoyuzhou playback progress pid did not match history eid ${eid}`);
        }
        const progressSec = optionalSeconds(row.progress, `progress in row ${index + 1}`);
        if (progressSec !== null && episode.durationSec !== null && progressSec > episode.durationSec) {
            throw new CommandExecutionError(`Xiaoyuzhou playback progress exceeded duration for eid ${eid}`);
        }
        progressById.set(eid, {
            progressSec,
            playedAt: optionalIsoTime(row.playedAt, `playedAt in row ${index + 1}`),
        });
    }
    for (const episode of episodes) {
        if (!progressById.has(episode.eid)) {
            throw new CommandExecutionError(
                `Xiaoyuzhou playback progress omitted requested eid ${episode.eid}; the history join is incomplete`,
            );
        }
    }
    return progressById;

View on GitHub (pinned to 49907e53dc)

Solutions

  1. Log the offending eid, the progress pid, and the history episode pid and compare against a raw API response.
  2. Re-fetch history and progress in one session so both responses come from the same API version.
  3. Remove any local transformation of episode.pid before passing episodes into parseProgressRows.
  4. If the API genuinely changed field semantics, update the parsing code to the new contract.

Example fix

// before
episodes.push({ eid: entry.eid, pid: entry.podcast.id.toUpperCase(), ... });
return parseProgressRows(rows, episodes);
// after
episodes.push({ eid: entry.eid, pid: entry.podcast.id, ... }); // keep pid exactly as returned
return parseProgressRows(rows, episodes);
Defensive patterns

Strategy: validation

Validate before calling

for (const r of progressRows) {
  const ep = episodesById.get(r.eid);
  if (ep && r.pid !== ep.pid) throw new Error(`pid mismatch for ${r.eid}: ${r.pid} != ${ep.pid}`);
}

Try / catch

try {
  const progress = progressById(episodes);
} catch (e) {
  if (e instanceof CommandExecutionError && /pid did not match/.test(e.message)) {
    console.error('History/progress join mismatch:', e.message);
  } else throw e;
}

Prevention

When it happens

Trigger: The progress API returns a pid for an eid that differs from the pid recorded in the history entry for that same eid — typically after an upstream API change, an eid/pid field swap, or stale/mixed responses from different endpoints.

Common situations: Server changes the meaning or casing of pid/eid fields; a proxy serves responses captured at different times; custom code mutates episode objects (e.g. remapping pids) before parsing; version skew between history and progress endpoints.

Related errors


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