{"record":{"id":"ae89cfe6a991162f","repo":"jackwener/OpenCLI","slug":"weread-in-book-search-returned-non-advancing-searc","errorCode":null,"errorMessage":"WeRead in-book search returned non-advancing searchIdx","messagePattern":"WeRead in-book search returned non-advancing searchIdx","errorType":"exception","errorClass":"CommandExecutionError","httpStatus":null,"severity":"error","filePath":"clis/weread/book-search.js","lineNumber":313,"sourceCode":"            const snippet = normalizeSearchText(item.abstract);\n            const searchIdx = parseOptionalFiniteNumber(item.searchIdx);\n            if (!snippet || searchIdx == null || searchIdx <= 0) {\n                throw new CommandExecutionError('WeRead in-book search returned malformed match');\n            }\n            return {\n                ...item,\n                abstract: snippet,\n                chapterIdx: parseOptionalFiniteNumber(item.chapterIdx),\n                chapterUid: parseOptionalFiniteNumber(item.chapterUid),\n                searchIdx,\n            };\n        });\n        if (result.length === 0)\n            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","sourceCodeStart":295,"sourceCodeEnd":331,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/weread/book-search.js#L295-L331","documentation":"searchWithinBook paginates by searchIdx: each page's last match must have a searchIdx strictly greater than the previous maxIdx. When the API returns a page whose last searchIdx <= maxIdx, the loop would fetch the same page forever, so the CLI throws CommandExecutionError instead.","triggerScenarios":"The in-book search endpoint returns a page whose final match's searchIdx does not advance past maxIdx — duplicated or re-sorted results, or a server bug after many pages.","commonSituations":"Deep pagination into large books where WeRead's index misbehaves; results re-ranked between requests; concurrent edits to the book shifting indices.","solutions":["Lower the limit so fewer pages are requested before the cursor misbehaves.","Retry the search — re-ranking between pages is often transient.","Add a page-count cap in your own wrapper and stop before the error.","Report the cursor bug if it reproduces consistently for one book/query."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"try {\n  return await searchWithinBook(bookId, query, { limit });\n} catch (e) {\n  if (e instanceof CommandExecutionError && e.message.includes('non-advancing searchIdx')) {\n    return searchWithinBook(bookId, query, { limit: Math.min(limit, 50) }); // smaller limit, fewer pages\n  }\n  throw e;\n}","preventionTips":["Keep limits modest so pagination stops before cursor instability.","Cap page count in wrapper code around the CLI.","Retry once on this error — result re-ranking is often transient."],"tags":["pagination","search","infinite-loop-guard"],"backgroundTag":"pagination-cursor-not-advancing","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}