jackwener/OpenCLI · error · CommandExecutionError
Could not parse a bookId from the reader URL
Error message
Could not parse a bookId from the reader URL
What it means
resolveBookTarget accepted the target as a well-formed reader URL, but loadReaderMetadata (which scrapes the page's window.__INITIAL_STATE__) returned metadata without a bookId. The CLI cannot identify the book, so it throws CommandExecutionError.
Source
Thrown at clis/weread/book-search.js:258
}
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))),
};
}
async function resolveBookTarget(target, bookRank) {
if (/^https?:\/\//i.test(target)) {
const readerUrl = parseWereadReaderUrl(target);
if (!readerUrl) {
throw new ArgumentError('book URL must be a https://weread.qq.com/web/reader/<id> URL');
}
const metadata = await loadReaderMetadata(readerUrl);
if (!metadata?.bookId) {
throw new CommandExecutionError('Could not parse a bookId from the reader URL');
}
return metadata;
}
if (/^\d+$/.test(target)) {
const metadata = {};
metadata.bookId = target;
metadata.title = '';
metadata.author = '';
metadata.readerUrl = '';
metadata.chapters = [];
return metadata;
}
return searchBookByQuery(target, bookRank);
}
async function searchWithinBook(bookId, query, limit, fragmentSize) {
const rows = [];
let maxIdx = 0;View on GitHub (pinned to 49907e53dc)
Solutions
- Retry with a valid logged-in session/cookie so the real book page loads.
- Verify the URL actually resolves to a live book (open it in a browser).
- If the page loads fine in a browser, inspect window.__INITIAL_STATE__ for schema changes and update the extractor.
- Pass the numeric bookId directly instead of the URL.
Defensive patterns
Strategy: retry
Type guard
function metadataHasBookId(m) {
return m != null && typeof m === 'object' && typeof m.bookId === 'string' && m.bookId.length > 0;
} Try / catch
try {
return await resolveBookTarget(url, rank);
} catch (e) {
if (e instanceof CommandExecutionError && e.message.includes('Could not parse a bookId')) {
await sleep(2000);
return resolveBookTarget(url, rank); // transient page / rate limit
}
throw e;
} Prevention
- Keep a valid logged-in session/cookie so WeRead serves the real book page.
- Throttle requests to avoid anti-bot interstitials.
- Prefer numeric bookId to skip page scraping entirely.
When it happens
Trigger: parseWereadReaderUrl succeeded but the fetched reader page's embedded initial state lacks bookId — e.g. WeRead returned a login/captcha/error page instead of the book page, or the page template changed.
Common situations: Not logged in or session expired so WeRead serves an interstitial; anti-bot / rate limiting serving degraded HTML; a WeRead frontend update renaming or removing the bookId field in __INITIAL_STATE__.
Related errors
- hotels.ctrip.com
- vacations.ctrip.com
- guazi ${contextHint} hit an anti-bot challenge — Guazi may h
- No chats visible in sidebar. Are you logged in?
- NOTEBOOKLM_TOKENS
AI-assisted analysis of jackwener/OpenCLI@49907e53dc (2026-08-29).
Data as JSON: /api/errors/88773d72b739f803.
Report an issue: GitHub.