jackwener/OpenCLI · error · CommandExecutionError

Xiaoyuzhou history returned an empty page with a continuatio

Error message

Xiaoyuzhou history returned an empty page with a continuation cursor

What it means

The parser treats a page carrying a loadMoreKey as a promise of more data, so an empty entries array alongside a continuation cursor is contradictory — continuing would loop forever or produce a duplicate-cursor error. The library fails fast with CommandExecutionError to protect the pagination loop invariant.

Source

Thrown at clis/xiaoyuzhou/history.js:82

    let next;
    if (Array.isArray(payload)) {
        entries = payload;
        next = response.raw?.loadMoreKey;
    }
    else if (isRecord(payload) && Array.isArray(payload.data)
        && !Object.prototype.hasOwnProperty.call(response.raw, 'loadMoreKey')) {
        entries = payload.data;
        next = payload.loadMoreKey;
    }
    else {
        throw new CommandExecutionError('Xiaoyuzhou history returned an unexpected response shape');
    }
    if (next == null || next === '') return { entries, next: null };
    if (typeof next !== 'string' || !next.trim()) {
        throw new CommandExecutionError('Xiaoyuzhou history returned an invalid loadMoreKey');
    }
    if (entries.length === 0) {
        throw new CommandExecutionError('Xiaoyuzhou history returned an empty page with a continuation cursor');
    }
    return { entries, next: next.trim() };
}

function parseHistoryEpisode(entry, rowNumber) {
    if (!isRecord(entry) || !isRecord(entry.episode)) {
        throw new CommandExecutionError(`Xiaoyuzhou history row ${rowNumber} is malformed; expected episode metadata`);
    }
    const episode = entry.episode;
    if (!isRecord(episode.podcast) || typeof episode.isFinished !== 'boolean') {
        throw new CommandExecutionError(`Xiaoyuzhou history row ${rowNumber} is missing podcast or finished state`);
    }
    return {
        eid: requiredId(episode.eid, `eid in row ${rowNumber}`),
        pid: requiredId(episode.pid, `pid in row ${rowNumber}`),
        title: requiredString(episode.title, `title in row ${rowNumber}`),
        podcast: requiredString(episode.podcast.title, `podcast title in row ${rowNumber}`),
        durationSec: optionalSeconds(episode.duration, `duration in row ${rowNumber}`, { positive: true }),

View on GitHub (pinned to 49907e53dc)

Solutions

  1. Re-run the command; a shifting server snapshot is often transient on retry.
  2. Reduce the page window (lower --limit or --max-pages) so fewer continuation requests are made before the snapshot changes.
  3. Wait a few minutes (e.g. after mass deletions) so the server-side history stabilizes, then retry.
  4. Update the CLI in case the API changed end-of-pagination semantics (cursor present but exhausted).
  5. If reproducible, capture the raw page and report the server-side cursor bug.

Example fix

// before (server bug)
{ "data": [], "loadMoreKey": "next-page" }
// after (expected)
{ "data": [] }  // or entries with a loadMoreKey
Defensive patterns

Strategy: retry

Try / catch

try {
  const rows = await runHistory();
} catch (e) {
  if (e.message === 'Xiaoyuzhou history returned an empty page with a continuation cursor') {
    // wait and retry once with a smaller --limit; the snapshot likely shifted
  } else throw e;
}

Prevention

When it happens

Trigger: A history page returns zero entries but still includes a non-empty loadMoreKey, encountered while paging through `xiaoyuzhou history` (especially with --all).

Common situations: The account's history was deleted/mutated between page requests so the snapshot shifted; the server has a bug at the end of the cursor chain; a very aggressive --max-pages interacts with a shifting server-side snapshot.

Related errors


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