{"record":{"id":"2739df96d867510e","repo":"jackwener/OpenCLI","slug":"weread-book-search-no-matches-for-query-in-b","errorCode":null,"errorMessage":"weread book-search: No matches for \"${query}\" in book ${bookId}","messagePattern":"weread book-search: No matches for \"(.+?)\" in book (.+?)","errorType":"exception","errorClass":"EmptyResultError","httpStatus":null,"severity":"warning","filePath":"clis/weread/book-search.js","lineNumber":327,"sourceCode":"            break;\n        rows.push(...result);\n        const lastSearchIdx = result[result.length - 1].searchIdx;\n        if (lastSearchIdx <= maxIdx)\n            throw new CommandExecutionError('WeRead in-book search returned non-advancing searchIdx');\n        maxIdx = lastSearchIdx;\n        if (rows.length >= limit)\n            break;\n        const hasMore = parseHasMore(data?.hasMore);\n        if (hasMore == null) {\n            if (result.length < pageSize)\n                break;\n            throw new CommandExecutionError('WeRead in-book search returned malformed pagination state');\n        }\n        if (!hasMore)\n            break;\n    }\n    if (rows.length === 0) {\n        throw new EmptyResultError('weread book-search', `No matches for \"${query}\" in book ${bookId}`);\n    }\n    return rows.slice(0, limit);\n}\n\nfunction buildChapterMap(chapters) {\n    const map = new Map();\n    for (const chapter of chapters) {\n        const chapterUid = parseOptionalFiniteNumber(chapter?.chapterUid);\n        if (chapterUid == null)\n            continue;\n        map.set(chapterUid, {\n            chapterIdx: parseOptionalFiniteNumber(chapter?.chapterIdx),\n            chapterTitle: normalizeSearchText(chapter?.title),\n        });\n    }\n    return map;\n}\n","sourceCodeStart":309,"sourceCodeEnd":345,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/weread/book-search.js#L309-L345","documentation":"searchWithinBook completed pagination without collecting any valid match rows, so it throws EmptyResultError stating the query and bookId. This is a normal 'nothing found' outcome, not a protocol failure — the query simply has no matches in that book.","triggerScenarios":"Calling searchWithinBook (via matches) with a query string that does not occur in the given book, or with mismatched bookId, or with a limit so small combined with filters that zero rows survive.","commonSituations":"Searching an English term in a Chinese book (or vice versa); typos; passing the wrong bookId after resolving the wrong book; expecting content that exists only in another edition.","solutions":["Verify the bookId corresponds to the intended book/edition.","Try a shorter or alternate-language keyword.","Search across all books (searchBookByQuery) instead of within one book.","Handle EmptyResultError in your caller as a normal empty result, not a crash."],"exampleFix":"// before\nconst rows = await matches(bookId, '机器学习');\n// after\ntry {\n  const rows = await matches(bookId, '机器学习');\n} catch (e) {\n  if (e instanceof EmptyResultError) return [];\n  throw e;\n}","handlingStrategy":"try-catch","validationCode":"// pre-check impossible without searching; ensure inputs are sane:\nif (!bookId || !/\\d+/.test(bookId)) throw new Error('invalid bookId');\nif (!query || !query.trim()) throw new Error('query required');","typeGuard":null,"tryCatchPattern":"try {\n  return await searchWithinBook(bookId, query);\n} catch (e) {\n  if (e instanceof EmptyResultError) {\n    return []; // no matches is a normal outcome\n  }\n  throw e;\n}","preventionTips":["Catch EmptyResultError explicitly and treat it as an empty set.","Verify bookId and book edition before searching within it.","Use shorter, language-appropriate keywords."],"tags":["empty-result","search","weread"],"backgroundTag":"empty-search-result","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T17:17:51.833Z"}