{"record":{"id":"c1f0bbf05b38d092","repo":"jackwener/OpenCLI","slug":"weread-in-book-search-returned-malformed-match","errorCode":null,"errorMessage":"WeRead in-book search returned malformed match","messagePattern":"WeRead in-book search returned malformed match","errorType":"exception","errorClass":"CommandExecutionError","httpStatus":null,"severity":"error","filePath":"clis/weread/book-search.js","lineNumber":293,"sourceCode":"    const rows = [];\n    let maxIdx = 0;\n    while (rows.length < limit) {\n        const remaining = limit - rows.length;\n        const pageSize = remaining < SEARCH_PAGE_SIZE ? remaining : SEARCH_PAGE_SIZE;\n        const url = new URL('/web/book/search', WEREAD_WEB_ORIGIN);\n        url.searchParams.set('bookId', bookId);\n        url.searchParams.set('keyword', query);\n        url.searchParams.set('maxIdx', String(maxIdx));\n        url.searchParams.set('count', String(pageSize));\n        url.searchParams.set('fragmentSize', String(fragmentSize));\n        url.searchParams.set('onlyCount', '0');\n        const data = await fetchJson(url, 'WeRead in-book search');\n        if (!Array.isArray(data?.result)) {\n            throw new CommandExecutionError('WeRead in-book search returned malformed result');\n        }\n        const result = data.result.map((item) => {\n            if (!item || typeof item !== 'object') {\n                throw new CommandExecutionError('WeRead in-book search returned malformed match');\n            }\n            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;","sourceCodeStart":275,"sourceCodeEnd":311,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/weread/book-search.js#L275-L311","documentation":"While mapping data.result, searchWithinBook requires each match to be a non-null object. This second variant of the 'malformed match' error fires when an entry of the result array is null or not an object, so abstract/searchIdx extraction would crash.","triggerScenarios":"The result array contains null, a string, or a number element — e.g. WeRead inserts placeholder/null entries into result for removed or censored matches.","commonSituations":"Books with recently edited/deleted chapters can yield null match slots; API schema drift; partial responses under rate limiting.","solutions":["Retry the search; transient nulls often disappear on a fresh query.","Narrow the query so problematic matches are excluded.","If reproducible, filter out non-object items client-side or update the parser to skip them."],"exampleFix":"// before\nconst result = data.result.map((item) => { ... });\n// after\nconst result = data.result.filter((item) => item && typeof item === 'object').map((item) => { ... });","handlingStrategy":"type-guard","validationCode":null,"typeGuard":"function isMatchObject(item) {\n  return item != null && typeof item === 'object' && !Array.isArray(item);\n}","tryCatchPattern":"try {\n  return await searchWithinBook(bookId, query);\n} catch (e) {\n  if (e instanceof CommandExecutionError && e.message.includes('malformed match')) {\n    return []; // tolerate degraded results\n  }\n  throw e;\n}","preventionTips":["Retry transient searches that return null entries.","Keep queries specific to reduce degraded matches.","Track WeRead API schema changes."],"tags":["api-schema","search","weread"],"backgroundTag":"api-response-schema-mismatch","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T17:17:51.833Z"}