thedotmack/claude-mem · warning
Failed to parse search results
Error message
Failed to parse search results: ${error instanceof Error ? error.message : String(error)} What it means
parseSearchResponse JSON-parses the raw text returned by the memory search endpoint and logs this warning when JSON.parse throws. It then returns the string 'Failed to parse search results.' instead of throwing, so the plugin returns a graceful failure message to the caller.
Solutions
- Log/inspect the raw response text passed to parseSearchResponse to see what was actually returned.
- Verify the worker/search service is healthy and returns JSON for search queries.
- Check HTTP status code before parsing and surface non-200 responses separately.
- Ensure no proxy or middleware rewrites the response body.
- Add a guard: skip JSON.parse when text is empty.
Example fix
// before
const parsed = parseSearchResponse(await res.text(), query);
// after
const text = await res.text();
if (!res.ok || !text.trim().startsWith('{')) {
throw new Error(`Search returned non-JSON response (HTTP ${res.status}): ${text.slice(0, 200)}`);
}
const parsed = parseSearchResponse(text, query); Defensive patterns
Strategy: validation
Validate before calling
function isParseableJson(text: string): boolean {
if (!text.trim()) return false;
try { JSON.parse(text); return true; } catch { return false; }
}
// call before: if (!isParseableJson(rawBody)) throw new Error('non-JSON search response'); Type guard
function isJsonObject(v: unknown): v is Record<string, unknown> {
return typeof v === 'object' && v !== null && !Array.isArray(v);
} Try / catch
try {
const data = JSON.parse(text);
if (!isJsonObject(data)) throw new TypeError('search response is not an object');
} catch (error) {
console.warn('[claude-mem] Failed to parse search results:', error instanceof Error ? error.message : String(error), 'body:', text.slice(0, 200));
return 'Failed to parse search results.';
} Prevention
- Check res.ok and Content-Type: application/json before parsing.
- Log the first 200 chars of any non-JSON body for diagnosis.
- Handle empty bodies explicitly before JSON.parse.
- Watch for proxies/interceptors that replace JSON with HTML error pages.
When it happens
Trigger: execute() or rendered() passes an HTTP response body (or other text) to parseSearchResponse and the body is not valid JSON — HTML error pages, empty bodies, truncated responses, or proxy/error intermediaries.
Common situations: Worker returned an HTML 502/404 page; response truncated mid-stream; server returned plain-text error message; middleware injecting non-JSON content.
Understand the failure class
Background: JSON parse error: "Unexpected token" / "not valid JSON" / "failed to parse" — what JSON parsers are really complaining about — this error's family across 45 libraries.
- Parsing and encoding errors: unexpected token, malformed input — why parsers reject input and how to find the real culprit.
Related errors
- Anthropic API error
- canonical content
- canonical content
- claude-mem: could not read
- claude-mem does not appear to be installed.
AI-assisted analysis of thedotmack/claude-mem@d8bc9755e7 (2026-09-17).
Data as JSON: /api/errors/787c11cd3bbeceeb.
Report an issue: GitHub.
Appendix: source
Thrown at src/integrations/opencode-plugin/index.ts:323
return parseSearchResponse(text, query);
},
},
},
};
};
/**
* The worker returns Claude-style `{ content: [{ type: 'text', text: '...' }] }`
* blocks, NOT `{ items: [...] }` (#2406). Concatenate the text blocks and return
* them verbatim; an empty block list or a "No observations found" body becomes a
* clear no-results message.
*/
export function parseSearchResponse(text: string, query: string): string {
let data: unknown;
try {
data = JSON.parse(text);
} catch (error: unknown) {
console.warn(
"[claude-mem] Failed to parse search results:",
error instanceof Error ? error.message : String(error),
);
return "Failed to parse search results.";
}
const content = (data as { content?: Array<{ type?: string; text?: string }> }).content;
if (!Array.isArray(content) || content.length === 0) {
return `No results found for "${query}".`;
}
const rendered = content
.filter((block) => block.type === "text" && typeof block.text === "string")
.map((block) => block.text as string)
.join("\n")
.trim();
if (!rendered) {View on GitHub (pinned to d8bc9755e7)