{"record":{"id":"99fa26cc9e3a528d","repo":"jackwener/OpenCLI","slug":"book-url-must-be-a-https-weread-qq-com-web-reade","errorCode":null,"errorMessage":"book URL must be a https://weread.qq.com/web/reader/<id> URL","messagePattern":"book URL must be a https://weread\\.qq\\.com/web/reader/<id> URL","errorType":"validation","errorClass":"ArgumentError","httpStatus":null,"severity":"error","filePath":"clis/weread/book-search.js","lineNumber":254,"sourceCode":"        chapters: [],\n    };\n    if (!selected.bookId) {\n        throw new CommandExecutionError(`WeRead book search result ${bookRank} is missing bookId`);\n    }\n    const htmlEntries = await loadSearchHtmlEntries(bookQuery);\n    selected.readerUrl = resolveReaderUrlForBook(selected, htmlEntries);\n    const readerMetadata = await loadReaderMetadata(selected.readerUrl);\n    return {\n        ...selected,\n        ...Object.fromEntries(Object.entries(readerMetadata ?? {}).filter(([, value]) => value != null && value !== '' && !(Array.isArray(value) && value.length === 0))),\n    };\n}\n\nasync function resolveBookTarget(target, bookRank) {\n    if (/^https?:\\/\\//i.test(target)) {\n        const readerUrl = parseWereadReaderUrl(target);\n        if (!readerUrl) {\n            throw new ArgumentError('book URL must be a https://weread.qq.com/web/reader/<id> URL');\n        }\n        const metadata = await loadReaderMetadata(readerUrl);\n        if (!metadata?.bookId) {\n            throw new CommandExecutionError('Could not parse a bookId from the reader URL');\n        }\n        return metadata;\n    }\n    if (/^\\d+$/.test(target)) {\n        const metadata = {};\n        metadata.bookId = target;\n        metadata.title = '';\n        metadata.author = '';\n        metadata.readerUrl = '';\n        metadata.chapters = [];\n        return metadata;\n    }\n    return searchBookByQuery(target, bookRank);\n}","sourceCodeStart":236,"sourceCodeEnd":272,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/weread/book-search.js#L236-L272","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Open the book in the WeRead web reader and copy the full https://weread.qq.com/web/reader/<id> URL from the address bar.","Or pass the numeric bookId directly instead of a URL.","Convert http:// to https:// and remove extra path segments/query params before calling."],"exampleFix":"// before\nresolveBookTarget('http://weread.qq.com/web/bookDetail/abc', 1)\n// after\nresolveBookTarget('https://weread.qq.com/web/reader/abc', 1)","handlingStrategy":"validation","validationCode":"function isWereadReaderUrl(u) {\n  try {\n    const url = new URL(u);\n    const parts = url.pathname.split('/').filter(Boolean);\n    return url.protocol === 'https:' && url.hostname === 'weread.qq.com' &&\n      parts[0] === 'web' && parts[1] === 'reader' && parts.length === 3 && !!parts[2];\n  } catch { return false; }\n}\nif (!isWereadReaderUrl(target)) throw new Error('not a weread reader URL');","typeGuard":"function isWereadReaderUrl(u) {\n  return /^https:\\/\\/weread\\.qq\\.com\\/web\\/reader\\/[^/?#]+$/.test(u);\n}","tryCatchPattern":null,"preventionTips":["Copy URLs only from the web reader address bar (https://weread.qq.com/web/reader/<id>).","Convert http:// to https:// and strip query/hash before passing.","Pass numeric bookId when you have it."],"tags":["argument-validation","url-parsing","weread"],"backgroundTag":"invalid-url-format","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}