jackwener/OpenCLI · error · ArgumentError

book URL must be a https://weread.qq.com/web/reader/<id> URL

Error message

book URL must be a https://weread.qq.com/web/reader/<id> URL

What it means

resolveBookTarget throws this ArgumentError when the target looks like a URL (matches /^https?:\/\//i) but parseWereadReaderUrl cannot normalize it to a https://weread.qq.com/web/reader/<id> URL. parseWereadReaderUrl rejects non-https schemes, other hostnames, wrong path shape, or extra path segments.

Source

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

        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))),
    };
}

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);
}

View on GitHub (pinned to 49907e53dc)

Solutions

  1. Open the book in the WeRead web reader and copy the full https://weread.qq.com/web/reader/<id> URL from the address bar.
  2. Or pass the numeric bookId directly instead of a URL.
  3. Convert http:// to https:// and remove extra path segments/query params before calling.

Example fix

// before
resolveBookTarget('http://weread.qq.com/web/bookDetail/abc', 1)
// after
resolveBookTarget('https://weread.qq.com/web/reader/abc', 1)
Defensive patterns

Strategy: validation

Validate before calling

function isWereadReaderUrl(u) {
  try {
    const url = new URL(u);
    const parts = url.pathname.split('/').filter(Boolean);
    return url.protocol === 'https:' && url.hostname === 'weread.qq.com' &&
      parts[0] === 'web' && parts[1] === 'reader' && parts.length === 3 && !!parts[2];
  } catch { return false; }
}
if (!isWereadReaderUrl(target)) throw new Error('not a weread reader URL');

Type guard

function isWereadReaderUrl(u) {
  return /^https:\/\/weread\.qq\.com\/web\/reader\/[^/?#]+$/.test(u);
}

Prevention

When it happens

Trigger: Passing a weread URL that is not a reader URL (e.g. https://weread.qq.com/web/bookDetail/xxx), an http:// URL, a mobile weread.qq.com/share/ link, or a reader URL with query/extra path segments.

Common situations: Copying a book detail or share link from the WeRead app instead of the web reader URL; stripping the hash fragment that apps put in reader URLs; shortening the URL.

Related errors


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