{"record":{"id":"697164a53e7f32b1","repo":"thedotmack/claude-mem","slug":"claude-mem-failed-to-parse-search-results","errorCode":null,"errorMessage":"[claude-mem] Failed to parse search results:","messagePattern":"\\[claude-mem\\] Failed to parse search results:","errorType":"console","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"src/integrations/opencode-plugin/index.ts","lineNumber":315,"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":297,"sourceCodeEnd":333,"githubUrl":"https://github.com/thedotmack/claude-mem/blob/e2d1df569a8f04075d40e92461128ece7cf04c82/src/integrations/opencode-plugin/index.ts#L297-L333","documentation":"A console.warn from the opencode plugin's parseSearchResponse: the worker's HTTP response body could not be JSON.parse'd at all. The plugin expects the worker's Claude-style { content: [{ type:'text', text }] } envelope; when the body is an HTML error page, a proxy message, or an empty/truncated response, parsing throws and the function degrades to a user-facing 'Failed to parse search results.' string.","triggerScenarios":"The opencode tool points at a wrong port/host where something else answers (returning HTML); a reverse proxy or captive portal intercepts the request; the response is chunk-truncated by a timeout; the worker URL includes a path prefix that 404s with an HTML body.","commonSituations":"CLAUDE_MEM_WORKER_PORT changed after the plugin was configured; plugin version expecting the #2406 content-block shape against an older worker returning { items: [...] }-style or plain text; localhost firewall/AV middleware injecting error pages.","solutions":["Verify the configured worker URL answers JSON: curl -s <base>/api/health","Match plugin and worker versions — the content-block response shape is version-dependent (#2406)","Log the raw text before parsing to see what actually came back, then fix the routing/port mismatch"],"exampleFix":"// before\nconst data = JSON.parse(text); // throws on HTML 404 page\n\n// after\nlet data: unknown;\ntry {\n  data = JSON.parse(text);\n} catch {\n  console.error('non-JSON worker response:', text.slice(0, 200));\n  return `Search unavailable (worker returned non-JSON).`;\n}","handlingStrategy":"fallback","validationCode":"function looksLikeWorkerJson(text: string): boolean {\n  const t = text.trimStart();\n  return t.startsWith('{') || t.startsWith('[');\n}","typeGuard":"interface SearchEnvelope { content?: Array<{ type?: string; text?: string }> }\nfunction isSearchEnvelope(data: unknown): data is SearchEnvelope {\n  return typeof data === 'object' && data !== null &&\n    Array.isArray((data as SearchEnvelope).content);\n}","tryCatchPattern":"let data: unknown;\ntry { data = JSON.parse(text); }\ncatch { return 'Search unavailable (worker returned non-JSON response).'; }\nif (!isSearchEnvelope(data) || data.content.length === 0)\n  return `No results found for \"${query}\".`;","preventionTips":["Health-check the configured worker URL before wiring the plugin into a session","Pin plugin and worker to compatible released versions (content-block shape, #2406)","Log the first 200 chars of unparseable bodies once — it identifies proxies/misroutes instantly"],"tags":["json-parse","opencode-plugin","search","response-shape","version-mismatch"],"backgroundTag":"invalid-json-response-body","analyzedSha":"e2d1df569a8f04075d40e92461128ece7cf04c82","analyzedAt":"2026-08-20T23:58:13.836Z","contentChangedAt":"2026-08-20T23:58:13.836Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}