PAL-BUG-2: `pal__analyze` intermittently returns `empty_response` after long body-read
Reporter: [email protected] (via /eval-models routing review)
First observed: 2026-W22 (2026-05-25, ~03:24Z)
Repro attempt: 2026-W23 (2026-05-25, ~18:38Z) — did NOT reproduce; intermittent
Repo: BeehiveInnovations/pal-mcp-server @ branch main
Severity: medium (workaround exists; intermittent symptom)
TL;DR
pal__analyze called with model=google/gemini-2.5-pro and a small (~6K tokens) attached file returned {"error":"No response from model","status":"empty_response"} after a 3-minute-42-second body read. OpenRouter's /v1/generation/{id} API confirmed the request reached the provider (a gen-id was minted) but all metadata fields (tokens, cost, finish_reason) were null.
On the W23 repro attempt with effectively the same prompt + fixture, the call succeeded with full output and normal metadata. The bug is intermittent.
W22 Evidence (the failing case)
Tool invocation (paraphrased — Master Claude with frozen test-prompts.md path):
pal__analyze(
step="Analyze the attached TypeScript codebase slice. Identify: (1) top 3 architectural risks, (2) 5 buggy files, (3) cross-file inconsistencies. Cite file:line for every finding.",
step_number=1, total_steps=1, next_step_required=False,
findings="Initial pass — long-context analysis.",
relevant_files=["/home/rabih/.claude/openrouter-models/test-fixtures/longcontext-50k.txt"],
model="google/gemini-2.5-pro",
analysis_type="architecture",
confidence="medium",
)Response from pal__analyze:
{"error":"No response from model","status":"empty_response"}Wall-clock latency: ~225 seconds before PAL gave up.
PAL log evidence (paraphrased): HTTP 200 received from OpenRouter, X-Generation-Id gen-1779687428-ty4eM7grau9sQ29iYsy1, response body started arriving but never completed — PAL's content parser saw empty content.
OpenRouter API check on the gen-id:
curl -H "Authorization: Bearer $KEY" https://openrouter.ai/api/v1/generation?id=gen-1779687428-ty4eM7grau9sQ29iYsy1 | jq .data
{
"model": null,
"tokens_prompt": null,
"tokens_completion": null,
"total_cost": null,
"generation_time": null,
"finish_reason": null,
"native_finish_reason": null,
"error": null
}OpenRouter has the request record but no completion fields — consistent with a stalled stream that never reached a stop frame.
W23 repro attempt (the passing case)
Same tool, same model, same fixture (longcontext-50k.txt), nearly-same prompt. Response: full ~4500-token analysis with provider_used: openrouter, finish_reason: stop, total_cost: 0.04276, generation_time: 36799 ms. Gen-id gen-1779734315-ZNiWkW13ptH95KTDwyUF.
The W22 vs W23 prompt differed only in trailing whitespace + a one-line summary directive — not enough to explain the symptom delta.
Hypothesis
Three candidate causes (not exhaustive):
- Provider-side stream stall on Gemini 2.5 Pro via OpenRouter. Some downstream node times out the stream after a long thinking burst but doesn't emit the final SSE
doneframe. PAL's parser, expecting a complete stream, ends with empty content. pal__analyzetool's wrapper system-prompt triggers Gemini thinking-token exhaustion. The analyze tool prepends an elaborate "expert analysis" system prompt; combined with thinking budget, output budget could starve to zero.- PAL's body-read timeout interacts badly with Gemini streaming via OpenRouter. PAL may have a
httpxtimeout that lets the connection idle long enough to half-close.
The chat-reroute workaround (route the same prompt via pal__chat) consistently works — suggesting the analyze tool's wrapper, not the underlying model, is in the failure path. But the W23 success makes it harder to nail down a specific config knob.
Workaround in use today
Re-route analyze prompts via pal__chat with the same model + the analyze system-prompt-modifier prepended to the user prompt. This bypasses pal__analyze's expert-validation wrapper.
Asks for upstream
- Add a server-side timeout-and-retry around the analyze tool's expert-validation HTTP call. If the response body has been idle for >60s with no content, abort and retry once.
- Surface the gen-id in the error response. Currently
{"error":"No response from model","status":"empty_response"}gives nothing diagnostic. Including the gen-id would let callers correlate against OpenRouter logs without scrapingmcp_server.log. - Investigate Gemini 2.5 Pro stream-stall pattern via OpenRouter. Check whether OpenRouter has known incidents around the affected timestamp (2026-05-25T03:24:00Z approx).
Cross-references
- W22 routing review:
~/.claude/openrouter-models/results/2026-W22.mdF2 + Failures section - W23 routing review:
~/.claude/openrouter-models/results/2026-W23.mdSection B (PAL-BUG-2) - Failing gen-id:
gen-1779687428-ty4eM7grau9sQ29iYsy1(epoch 1779687428 = 2026-05-25T03:23:48Z) - Passing gen-id:
gen-1779734315-ZNiWkW13ptH95KTDwyUF(epoch 1779734315 = 2026-05-25T15:38:35Z)
Source: BeehiveInnovations/pal-mcp-server