jackwener/OpenCLI · error · CommandExecutionError
Xiaoyuzhou playback progress returned duplicate eid ${eid}
Error message
Xiaoyuzhou playback progress returned duplicate eid ${eid} What it means
parseProgressRows joins a playback-progress API response against episodes already collected from history. Every progress row must reference a requested episode (eid), and each eid may appear at most once. This error means the progress endpoint returned the same eid twice, so the join would be ambiguous about which row's progress is authoritative.
Source
Thrown at clis/xiaoyuzhou/history.js:122
}
function parseProgressRows(response, episodes) {
if (!Array.isArray(response?.data)) {
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`,View on GitHub (pinned to 49907e53dc)
Solutions
- Deduplicate progress rows by eid before calling parseProgressRows, keeping the row with the latest playedAt or highest progress.
- Check whether your pagination loop overlaps pages and skip already-seen eids between pages.
- Verify with a raw request that duplicates come from the server, then report/patch against the upstream change.
- If using cached or recorded fixtures, regenerate them from a live response.
Example fix
// before
const rows = progressResponse.data.list;
return parseProgressRows(rows, episodes);
// after
const seen = new Set();
const rows = progressResponse.data.list.filter(r => {
if (seen.has(r.eid)) return false;
seen.add(r.eid);
return true;
});
return parseProgressRows(rows, episodes); Defensive patterns
Strategy: validation
Validate before calling
function assertUniqueEids(rows) {
const seen = new Set();
for (const r of rows) {
if (seen.has(r.eid)) throw new Error(`duplicate eid ${r.eid}`);
seen.add(r.eid);
}
}
assertUniqueEids(progressRows); Type guard
const hasUniqueEids = (rows) => new Set(rows.map(r => r.eid)).size === rows.length;
Prevention
- Deduplicate progress rows by eid before parsing.
- Avoid overlapping cursor pages or skip already-seen eids.
- Regenerate cached/recorded fixtures from live responses periodically.
When it happens
Trigger: The Xiaoyuzhou progress API returns two rows with the same eid for one fetch, e.g. a server-side bug, duplicated page data, or overlapping pagination whose rows are concatenated client-side before parsing.
Common situations: Upstream API schema changes introducing duplicate rows; mocking/recording proxies replaying responses; hand-crafted fixtures with repeated eids; paginated responses overlapping so the same progress row is fetched twice.
Related errors
- Xiaoyuzhou playback progress omitted requested eid ${episode
- Zhihu answer comments returned conflicting data for comment
- Bilibili view API did not return pages[] for --page selectio
- Bilibili view API returned duplicate page entries for p=${pa
- 分P 序号超出范围:p=${pageNum}(该视频共 ${total} 集)
AI-assisted analysis of jackwener/OpenCLI@49907e53dc (2026-08-29).
Data as JSON: /api/errors/eff41631521fdada.
Report an issue: GitHub.