jackwener/OpenCLI · error · CommandExecutionError

WeRead in-book search returned malformed pagination state

Error message

WeRead in-book search returned malformed pagination state

What it means

When a full page is returned but data.hasMore cannot be interpreted as true/false/1/0/'1'/'0', parseHasMore returns null. Since the page was full (length === pageSize) the loop would otherwise continue blindly; the CLI throws CommandExecutionError because it cannot determine whether more pages exist.

Source

Thrown at clis/weread/book-search.js:321

                chapterIdx: parseOptionalFiniteNumber(item.chapterIdx),
                chapterUid: parseOptionalFiniteNumber(item.chapterUid),
                searchIdx,
            };
        });
        if (result.length === 0)
            break;
        rows.push(...result);
        const lastSearchIdx = result[result.length - 1].searchIdx;
        if (lastSearchIdx <= maxIdx)
            throw new CommandExecutionError('WeRead in-book search returned non-advancing searchIdx');
        maxIdx = lastSearchIdx;
        if (rows.length >= limit)
            break;
        const hasMore = parseHasMore(data?.hasMore);
        if (hasMore == null) {
            if (result.length < pageSize)
                break;
            throw new CommandExecutionError('WeRead in-book search returned malformed pagination state');
        }
        if (!hasMore)
            break;
    }
    if (rows.length === 0) {
        throw new EmptyResultError('weread book-search', `No matches for "${query}" in book ${bookId}`);
    }
    return rows.slice(0, limit);
}

function buildChapterMap(chapters) {
    const map = new Map();
    for (const chapter of chapters) {
        const chapterUid = parseOptionalFiniteNumber(chapter?.chapterUid);
        if (chapterUid == null)
            continue;
        map.set(chapterUid, {
            chapterIdx: parseOptionalFiniteNumber(chapter?.chapterIdx),

View on GitHub (pinned to 49907e53dc)

Solutions

  1. Inspect the raw response to see how hasMore is now represented and update parseHasMore.
  2. Reduce the query scope/limit so fewer full pages are fetched.
  3. Retry if the response was a transient degraded envelope (auth/rate limit).

Example fix

// before
if (value === true || value === 1 || value === '1') return true;
// after (accept 'true'/'false' strings)
if (value === 'true' || value === true || value === 1 || value === '1') return true;
Defensive patterns

Strategy: try-catch

Type guard

function parseHasMoreSafe(v) {
  if (v === true || v === 1 || v === '1' || v === 'true') return true;
  if (v === false || v === 0 || v === '0' || v === 'false') return false;
  return null;
}

Try / catch

try {
  return await searchWithinBook(bookId, query);
} catch (e) {
  if (e instanceof CommandExecutionError && e.message.includes('malformed pagination state')) {
    return rowsSoFar; // accept what was collected
  }
  throw e;
}

Prevention

When it happens

Trigger: A full pageSize page arrives but data.hasMore is undefined, null, or an unexpected type (e.g. boolean-like string 'true', or the field renamed) — so pagination state is undecidable.

Common situations: WeRead API changing hasMore's representation (e.g. to 'true'/'false' strings or moving it into a nested object); error envelopes that keep result but drop hasMore.

Understand the failure class

Related errors


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