jackwener/OpenCLI · error · CommandExecutionError
WeRead in-book search returned malformed match
Error message
WeRead in-book search returned malformed match
What it means
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.
Source
Thrown at clis/weread/book-search.js:293
const rows = [];
let maxIdx = 0;
while (rows.length < limit) {
const remaining = limit - rows.length;
const pageSize = remaining < SEARCH_PAGE_SIZE ? remaining : SEARCH_PAGE_SIZE;
const url = new URL('/web/book/search', WEREAD_WEB_ORIGIN);
url.searchParams.set('bookId', bookId);
url.searchParams.set('keyword', query);
url.searchParams.set('maxIdx', String(maxIdx));
url.searchParams.set('count', String(pageSize));
url.searchParams.set('fragmentSize', String(fragmentSize));
url.searchParams.set('onlyCount', '0');
const data = await fetchJson(url, 'WeRead in-book search');
if (!Array.isArray(data?.result)) {
throw new CommandExecutionError('WeRead in-book search returned malformed result');
}
const result = data.result.map((item) => {
if (!item || typeof item !== 'object') {
throw new CommandExecutionError('WeRead in-book search returned malformed match');
}
const snippet = normalizeSearchText(item.abstract);
const searchIdx = parseOptionalFiniteNumber(item.searchIdx);
if (!snippet || searchIdx == null || searchIdx <= 0) {
throw new CommandExecutionError('WeRead in-book search returned malformed match');
}
return {
...item,
abstract: snippet,
chapterIdx: parseOptionalFiniteNumber(item.chapterIdx),
chapterUid: parseOptionalFiniteNumber(item.chapterUid),
searchIdx,
};
});
if (result.length === 0)
break;
rows.push(...result);
const lastSearchIdx = result[result.length - 1].searchIdx;View on GitHub (pinned to 49907e53dc)
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.
Example fix
// before
const result = data.result.map((item) => { ... });
// after
const result = data.result.filter((item) => item && typeof item === 'object').map((item) => { ... }); Defensive patterns
Strategy: type-guard
Type guard
function isMatchObject(item) {
return item != null && typeof item === 'object' && !Array.isArray(item);
} Try / catch
try {
return await searchWithinBook(bookId, query);
} catch (e) {
if (e instanceof CommandExecutionError && e.message.includes('malformed match')) {
return []; // tolerate degraded results
}
throw e;
} Prevention
- Retry transient searches that return null entries.
- Keep queries specific to reduce degraded matches.
- Track WeRead API schema changes.
When it happens
Trigger: 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.
Common situations: Books with recently edited/deleted chapters can yield null match slots; API schema drift; partial responses under rate limiting.
Understand the failure class
- Parsing and encoding errors: unexpected token, malformed input — why parsers reject input and how to find the real culprit.
Related errors
- WeRead book search result ${bookRank} is missing bookId
- WeRead in-book search returned malformed result
- book-rank must be <= ${books.length}
- WeRead in-book search returned malformed pagination state
- weread book-search: No matches for "${query}" in book ${book
AI-assisted analysis of jackwener/OpenCLI@49907e53dc (2026-08-29).
Data as JSON: /api/errors/c1f0bbf05b38d092.
Report an issue: GitHub.