jackwener/OpenCLI · error · CommandExecutionError
Xiaoyuzhou history pagination repeated the same cursor
Error message
Xiaoyuzhou history pagination repeated the same cursor
What it means
During pagination, fetchHistory guards against a next-cursor that equals the special 'load more' sentinel or one already visited. Repeating a cursor would loop forever fetching the same page, so the library aborts with this error.
Source
Thrown at clis/xiaoyuzhou/history.js:215
durationSec: episode.durationSec,
progressSec: progress.progressSec,
progressPct: episode.durationSec !== null && progress.progressSec !== null
? Number(((progress.progressSec / episode.durationSec) * 100).toFixed(1))
: null,
playedAt: progress.playedAt,
pubDate: episode.pubDate,
finished: episode.finished,
url: `https://www.xiaoyuzhoufm.com/episode/${episode.eid}`,
});
}
}
if (!fetchAll && rows.length >= limit) break;
if (page.next === null) {
exhausted = true;
break;
}
if (page.next === loadMoreKey || seenCursors.has(page.next)) {
throw new CommandExecutionError('Xiaoyuzhou history pagination repeated the same cursor');
}
if (pageNumber === maxPages) {
throw new CommandExecutionError(
`Xiaoyuzhou history stopped at the --max-pages safety limit (${maxPages}) before reaching the end`,
);
}
seenCursors.add(page.next);
loadMoreKey = page.next;
}
if (rows.length === 0) {
throw new EmptyResultError('xiaoyuzhou history', 'The logged-in account has no playback history');
}
if (fetchAll && !exhausted) {
throw new CommandExecutionError('Xiaoyuzhou history archive did not reach the end of pagination');
}
return rows;
}View on GitHub (pinned to 49907e53dc)
Solutions
- Log the raw page responses around the failure to see the duplicated cursor value and whether it is the sentinel.
- Check for an API change in cursor format and update cursor handling/encoding accordingly.
- Disable response caching/proxying so each page request hits the live API.
- Report the looping cursor to the API provider if the live service is at fault.
Example fix
// before
const next = page.next; // used as-is
// after (API changed cursor encoding)
const next = typeof page.next === 'string' && !page.next.startsWith('enc:')
? 'enc:' + btoa(page.next)
: page.next; Defensive patterns
Strategy: retry
Validate before calling
const seenCursors = new Set();
if (page.next && seenCursors.has(page.next)) throw new Error('cursor loop detected before continuing');
seenCursors.add(page.next); Try / catch
try {
const rows = await fetchHistory(args);
} catch (e) {
if (e instanceof CommandExecutionError && /repeated the same cursor/.test(e.message)) {
console.error('Upstream cursor bug or format change; disable caches and inspect cursors');
} else throw e;
} Prevention
- Disable caching proxies/middleware for paginated API calls.
- Update cursor encoding promptly when the API format changes.
- Log cursors per page to diagnose loops quickly.
When it happens
Trigger: The API returns the same next cursor twice in a row, returns a cursor equal to the current loadMoreKey, or a response shape change makes page.next stale/unchanged; also triggered when the loadMore sentinel value is echoed back as a real cursor.
Common situations: Upstream cursor-encoding changes (e.g. cursor now needs base64 wrapping); server bugs returning the last page's cursor again; proxies replaying cached responses; mock servers always returning a fixed next cursor.
Related errors
- Bilibili view API did not return pages[] for --page selectio
- 分P 序号超出范围:p=${pageNum}(该视频共 ${total} 集)
- Jike notifications pagination returned a repeated cursor
- Jike search API returned a malformed pagination cursor
- Jike search pagination returned a repeated cursor
AI-assisted analysis of jackwener/OpenCLI@49907e53dc (2026-08-29).
Data as JSON: /api/errors/7974cec0fbead5ea.
Report an issue: GitHub.