{"record":{"id":"64bfda00f842e5ae","repo":"jackwener/OpenCLI","slug":"weread-in-book-search-returned-malformed-paginatio","errorCode":null,"errorMessage":"WeRead in-book search returned malformed pagination state","messagePattern":"WeRead in-book search returned malformed pagination state","errorType":"exception","errorClass":"CommandExecutionError","httpStatus":null,"severity":"error","filePath":"clis/weread/book-search.js","lineNumber":321,"sourceCode":"                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\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),","sourceCodeStart":303,"sourceCodeEnd":339,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/weread/book-search.js#L303-L339","documentation":"When a full page is returned but data.hasMore cannot be interpreted as true/false/1/0/'1'/'0', parseHasMore returns null. Since the page was full (length === pageSize) the loop would otherwise continue blindly; the CLI throws CommandExecutionError because it cannot determine whether more pages exist.","triggerScenarios":"A full pageSize page arrives but data.hasMore is undefined, null, or an unexpected type (e.g. boolean-like string 'true', or the field renamed) — so pagination state is undecidable.","commonSituations":"WeRead API changing hasMore's representation (e.g. to 'true'/'false' strings or moving it into a nested object); error envelopes that keep result but drop hasMore.","solutions":["Inspect the raw response to see how hasMore is now represented and update parseHasMore.","Reduce the query scope/limit so fewer full pages are fetched.","Retry if the response was a transient degraded envelope (auth/rate limit)."],"exampleFix":"// before\nif (value === true || value === 1 || value === '1') return true;\n// after (accept 'true'/'false' strings)\nif (value === 'true' || value === true || value === 1 || value === '1') return true;","handlingStrategy":"try-catch","validationCode":null,"typeGuard":"function parseHasMoreSafe(v) {\n  if (v === true || v === 1 || v === '1' || v === 'true') return true;\n  if (v === false || v === 0 || v === '0' || v === 'false') return false;\n  return null;\n}","tryCatchPattern":"try {\n  return await searchWithinBook(bookId, query);\n} catch (e) {\n  if (e instanceof CommandExecutionError && e.message.includes('malformed pagination state')) {\n    return rowsSoFar; // accept what was collected\n  }\n  throw e;\n}","preventionTips":["Reduce page pressure: smaller limits mean fewer full pages.","Watch for hasMore representation changes in the WeRead API.","Treat results already collected as usable when pagination is undecidable."],"tags":["pagination","api-schema","search"],"backgroundTag":"api-response-schema-mismatch","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}