{"record":{"id":"6cd5e27650367bfc","repo":"rohitg00/agentmemory","slug":"this-name-returned-unexpected-response-json","errorCode":null,"errorMessage":"${this.name} returned unexpected response: ${JSON.stringify(data).slice(0, 200)}","messagePattern":"(.+?) returned unexpected response: (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"src/providers/openrouter.ts","lineNumber":66,"sourceCode":"        messages: [\n          { role: \"system\", content: systemPrompt },\n          { role: \"user\", content: userPrompt },\n        ],\n      }),\n    });\n\n    if (!response.ok) {\n      const text = await response.text();\n      throw new Error(`${this.name} API error (${response.status}): ${text}`);\n    }\n\n    const data = (await response.json()) as Record<string, unknown>;\n    const choices = data.choices as\n      | Array<{ message: { content: string } }>\n      | undefined;\n    const content = choices?.[0]?.message?.content;\n    if (!content) {\n      throw new Error(\n        `${this.name} returned unexpected response: ${JSON.stringify(data).slice(0, 200)}`,\n      );\n    }\n    return content;\n  }\n}\n","sourceCodeStart":48,"sourceCodeEnd":73,"githubUrl":"https://github.com/rohitg00/agentmemory/blob/e04ba88819c365c9acf9d6661ea802143e728bd6/src/providers/openrouter.ts#L48-L73","documentation":"OpenRouterProvider.call parses data.choices[0].message.content from the response and throws this if the content is missing or empty, including a 200-char JSON dump of the body. Since this.name is usually 'resilient(openrouter(...))' or the model name, the message identifies which provider returned the malformed payload. It catches 2xx responses whose payload doesn't match the Chat Completions shape.","triggerScenarios":"compress()/summarize() where OpenRouter returns 200 with choices absent, empty, or message.content empty — commonly when the upstream model returns only an error embedded in a 200 body, content is filtered, or the chosen slug routes to a non-chat endpoint.","commonSituations":"Free OpenRouter model tiers returning 200 with an in-body error and no choices; moderation/empty completion from the upstream model; OpenRouter routing a request to a backend that omits message.content; truncation settings producing empty content.","solutions":["Read the JSON dump in the message — check for nested OpenRouter error metadata","Switch config.model to a paid/stable OpenRouter model slug instead of a flaky free tier","Retry — transient upstream failures often return empty choices once","Reduce/adjust the prompt if the upstream model refuses or filters the content","Use the resilient wrapper's fallback chain to fail over to another provider"],"exampleFix":"// before\nmodel: 'meta-llama/llama-3-70b-instruct:free' // 200, empty choices, in-body error\n\n// after\nmodel: 'meta-llama/llama-3.1-70b-instruct'","handlingStrategy":"retry","validationCode":"if (!config.model?.includes('/')) throw new Error(`Invalid OpenRouter model slug: ${config.model}`);","typeGuard":"interface OpenRouterResponse { choices?: Array<{ message?: { content?: string } }> }\nfunction hasOpenRouterContent(r: OpenRouterResponse): boolean {\n  return typeof r.choices?.[0]?.message?.content === 'string' && r.choices[0].message.content.length > 0;\n}","tryCatchPattern":"try {\n  return await provider.call(prompt);\n} catch (e) {\n  if ((e as Error).message.includes('returned unexpected response')) {\n    return retryWithBackoff(() => provider.call(prompt), 2)\n      .catch(() => fallbackProvider.call(prompt));\n  }\n  throw e;\n}","preventionTips":["Prefer paid/stable OpenRouter models over flaky :free tiers for background jobs","Fail over on empty-choices 200s using createFallbackProvider","Inspect in-body error metadata in the thrown dump when it recurs","Retry once before failing over — empty choices is often transient upstream churn"],"tags":["openrouter","response-parsing","schema-mismatch"],"backgroundTag":"unexpected-response-schema","analyzedSha":"e04ba88819c365c9acf9d6661ea802143e728bd6","analyzedAt":"2026-08-30T01:07:40.754Z","schemaVersion":2},"datasetVersion":"2026-08-30T03:17:51.788Z"}