{"record":{"id":"787c11cd3bbeceeb","repo":"thedotmack/claude-mem","slug":"failed-to-parse-search-results-error-instanceof-error-error","errorCode":null,"errorMessage":"Failed to parse search results: ${error instanceof Error ? error.message : String(error)}","messagePattern":"Failed to parse search results: (.+?)","errorType":"console","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"src/integrations/opencode-plugin/index.ts","lineNumber":323,"sourceCode":"          return parseSearchResponse(text, query);\n        },\n      },\n    },\n  };\n};\n\n/**\n * The worker returns Claude-style `{ content: [{ type: 'text', text: '...' }] }`\n * blocks, NOT `{ items: [...] }` (#2406). Concatenate the text blocks and return\n * them verbatim; an empty block list or a \"No observations found\" body becomes a\n * clear no-results message.\n */\nexport function parseSearchResponse(text: string, query: string): string {\n  let data: unknown;\n  try {\n    data = JSON.parse(text);\n  } catch (error: unknown) {\n    console.warn(\n      \"[claude-mem] Failed to parse search results:\",\n      error instanceof Error ? error.message : String(error),\n    );\n    return \"Failed to parse search results.\";\n  }\n\n  const content = (data as { content?: Array<{ type?: string; text?: string }> }).content;\n  if (!Array.isArray(content) || content.length === 0) {\n    return `No results found for \"${query}\".`;\n  }\n\n  const rendered = content\n    .filter((block) => block.type === \"text\" && typeof block.text === \"string\")\n    .map((block) => block.text as string)\n    .join(\"\\n\")\n    .trim();\n\n  if (!rendered) {","sourceCodeStart":305,"sourceCodeEnd":341,"githubUrl":"https://github.com/thedotmack/claude-mem/blob/d8bc9755e74915e5c3b999181e10a67c889bce2a/src/integrations/opencode-plugin/index.ts#L305-L341","documentation":"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.","triggerScenarios":"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.","commonSituations":"Worker returned an HTML 502/404 page; response truncated mid-stream; server returned plain-text error message; middleware injecting non-JSON content.","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."],"exampleFix":"// before\nconst parsed = parseSearchResponse(await res.text(), query);\n// after\nconst text = await res.text();\nif (!res.ok || !text.trim().startsWith('{')) {\n  throw new Error(`Search returned non-JSON response (HTTP ${res.status}): ${text.slice(0, 200)}`);\n}\nconst parsed = parseSearchResponse(text, query);","handlingStrategy":"validation","validationCode":"function isParseableJson(text: string): boolean {\n  if (!text.trim()) return false;\n  try { JSON.parse(text); return true; } catch { return false; }\n}\n// call before: if (!isParseableJson(rawBody)) throw new Error('non-JSON search response');","typeGuard":"function isJsonObject(v: unknown): v is Record<string, unknown> {\n  return typeof v === 'object' && v !== null && !Array.isArray(v);\n}","tryCatchPattern":"try {\n  const data = JSON.parse(text);\n  if (!isJsonObject(data)) throw new TypeError('search response is not an object');\n} catch (error) {\n  console.warn('[claude-mem] Failed to parse search results:', error instanceof Error ? error.message : String(error), 'body:', text.slice(0, 200));\n  return 'Failed to parse search results.';\n}","preventionTips":["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."],"tags":["json","parse","search","plugin"],"backgroundTag":"json-parse-error","analyzedSha":"d8bc9755e74915e5c3b999181e10a67c889bce2a","analyzedAt":"2026-09-17T16:40:26.182Z","contentChangedAt":"2026-09-17T16:40:26.182Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}