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

  1. Log/inspect the raw response text passed to parseSearchResponse to see what was actually returned.
  2. Verify the worker/search service is healthy and returns JSON for search queries.
  3. Check HTTP status code before parsing and surface non-200 responses separately.
  4. Ensure no proxy or middleware rewrites the response body.
  5. 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

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.

Related errors


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)