jackwener/OpenCLI · error · CommandExecutionError

Xiaoyuzhou history repeated eid ${episode.eid}; the paginati

Error message

Xiaoyuzhou history repeated eid ${episode.eid}; the pagination snapshot may have changed and event-vs-overlap semantics are ambiguous

What it means

fetchHistory paginates the history feed and tracks every eid it has seen. If the same eid appears on a later page, the pagination snapshot changed between requests (new items inserted shifted entries across pages), making it ambiguous whether repeats are distinct play events or overlap artifacts. The library throws rather than guessing event-vs-overlap semantics.

Source

Thrown at clis/xiaoyuzhou/history.js:172

    let credentials = loadXiaoyuzhouCredentials();
    const seenEpisodeIds = new Set();
    const seenCursors = new Set();
    const rows = [];
    let loadMoreKey = null;
    let exhausted = false;

    for (let pageNumber = 1; pageNumber <= maxPages; pageNumber += 1) {
        const historyResponse = await requestXiaoyuzhouJson(HISTORY_ENDPOINT, {
            method: 'POST',
            body: loadMoreKey === null ? {} : { loadMoreKey },
            credentials,
        });
        credentials = historyResponse.credentials;
        const page = parseHistoryPage(historyResponse);
        const episodes = page.entries.map((entry, index) => parseHistoryEpisode(entry, index + 1));
        for (const episode of episodes) {
            if (seenEpisodeIds.has(episode.eid)) {
                throw new CommandExecutionError(
                    `Xiaoyuzhou history repeated eid ${episode.eid}; the pagination snapshot may have changed and event-vs-overlap semantics are ambiguous`,
                );
            }
            seenEpisodeIds.add(episode.eid);
        }

        const remaining = fetchAll ? episodes.length : limit - rows.length;
        const selected = episodes.slice(0, remaining);
        if (selected.length > 0) {
            const progressResponse = await requestXiaoyuzhouJson(PROGRESS_ENDPOINT, {
                method: 'POST',
                body: { eids: selected.map((episode) => episode.eid) },
                credentials,
            });
            credentials = progressResponse.credentials;
            const progressById = parseProgressRows(progressResponse, selected);
            for (const episode of selected) {
                const progress = progressById.get(episode.eid);

View on GitHub (pinned to 49907e53dc)

Solutions

  1. Retry the full fetch (ideally with --all) during a quiet period so the server snapshot is stable between pages.
  2. Confirm the API supports a snapshot/anchor parameter and pass it so pages come from one consistent snapshot.
  3. If you only need recent history, use --limit instead of paginating the whole archive, reducing the window for changes.
  4. Reduce time between page requests (avoid long delays/rate-limit backoff that widen the change window).

Example fix

// before
await fetchHistory({ all: true }); // long unattended run, may abort
// after
let rows;
for (let attempt = 0; attempt < 3; attempt++) {
  try { rows = await fetchHistory({ all: true }); break; }
  catch (e) {
    if (!/repeated eid/.test(e.message) || attempt === 2) throw e;
    await new Promise(r => setTimeout(r, 2000));
  }
}
Defensive patterns

Strategy: retry

Try / catch

for (let i = 0; i < 3; i++) {
  try { return await fetchHistory({ all: true }); }
  catch (e) {
    if (/repeated eid/.test(e.message) && i < 2) { await new Promise(r => setTimeout(r, 2000)); continue; }
    throw e;
  }
}

Prevention

When it happens

Trigger: New history entries are added server-side while paginating, shifting later entries onto already-fetched positions; cursor-based pagination without snapshot isolation; repeated loadMore cursors returning overlapping data.

Common situations: An active account playing episodes during a long --all fetch; APIs without snapshot isolation; clock skew between client and server; very old recorded fixtures replayed with shifting data.

Related errors


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