jackwener/OpenCLI · error · ArgumentError

book-rank must be <= ${books.length}

Error message

book-rank must be <= ${books.length}

What it means

searchBookByQuery throws this ArgumentError when the --book-rank supplied by the caller exceeds the number of results the WeRead book search API returned. It is a guard before indexing books[bookRank - 1] so the CLI never silently selects an undefined entry. The message includes the valid upper bound so the caller can pick a rank in range.

Source

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

            return exact[0].readerUrl;
    }
    const sameTitle = htmlEntries.filter((entry) => normalizeSearchText(entry.title) === title);
    return sameTitle.length === 1 ? sameTitle[0].readerUrl : '';
}

async function searchBookByQuery(bookQuery, bookRank) {
    const url = new URL('/web/search/global', `${WEREAD_WEB_ORIGIN}/web`);
    url.searchParams.set('keyword', bookQuery);
    const data = await fetchJson(url, 'WeRead book search');
    if (!Array.isArray(data?.books)) {
        throw new CommandExecutionError('WeRead book search returned malformed books');
    }
    const books = data.books;
    if (books.length === 0) {
        throw new EmptyResultError('weread book-search', `No WeRead books found for "${bookQuery}"`);
    }
    if (bookRank > books.length) {
        throw new ArgumentError(`book-rank must be <= ${books.length}`, `Only ${books.length} book search result(s) were returned for "${bookQuery}"`);
    }
    const bookInfo = books[bookRank - 1]?.bookInfo ?? {};
    const selected = {
        bookId: normalizeSearchText(bookInfo.bookId),
        title: normalizeSearchText(bookInfo.title),
        author: normalizeSearchText(bookInfo.author),
        readerUrl: '',
        chapters: [],
    };
    if (!selected.bookId) {
        throw new CommandExecutionError(`WeRead book search result ${bookRank} is missing bookId`);
    }
    const htmlEntries = await loadSearchHtmlEntries(bookQuery);
    selected.readerUrl = resolveReaderUrlForBook(selected, htmlEntries);
    const readerMetadata = await loadReaderMetadata(selected.readerUrl);
    return {
        ...selected,
        ...Object.fromEntries(Object.entries(readerMetadata ?? {}).filter(([, value]) => value != null && value !== '' && !(Array.isArray(value) && value.length === 0))),

View on GitHub (pinned to 49907e53dc)

Solutions

  1. Lower book-rank to a value between 1 and the count reported in the error message.
  2. Refine the search query (exact title, author, or Chinese text) so more/better results are returned and the desired book is within range.
  3. Pass the book URL or raw bookId instead of a rank to select the book deterministically.

Example fix

// before
cli book search '深度学习' --rank 10
// after (only 3 results returned)
cli book search '深度学习' --rank 3
Defensive patterns

Strategy: validation

Validate before calling

// no local count is known before the call, but clamp interactively:
const rank = Number(opts['book-rank']);
if (!Number.isInteger(rank) || rank < 1 || rank > 20) {
  throw new Error('book-rank must be a small positive integer; run the search first to see the count');
}

Try / catch

try {
  const book = await book(target, rank);
} catch (e) {
  if (/book-rank must be <= /.test(e.message)) {
    const max = Number(e.message.match(/<= (\d+)/)?.[1] ?? 0);
    return book(target, Math.min(rank, max));
  }
  throw e;
}

Prevention

When it happens

Trigger: Calling resolveBookTarget/book with a numeric rank greater than data.books.length after a successful search — e.g. book-rank 5 when the query 'x' matched only 2 books.

Common situations: Developers assume a popular-sounding query returns many hits; WeRead's search returns few or zero relevant results, so a hardcoded rank overflows. Also happens when the query is misspelled or in the wrong language, shrinking the result list.

Related errors


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