Halluminate prod status

← back to the counter

A string prompt closed the Agent SDK control channel, silently scoring every agentic verifier 0.0

Date 2026-08-04
Severity SEV-2 — core grading path broken (silent)
Outage window 05:05 – ~19:25 UTC (Aug 4)
Duration ~14h 20m, silent throughout
Detected by Wyatt — noticed every agentic verifier returning exactly 0.0
Prod breaker Victoria and Rob — self-reported, worn with pride
Status Resolved — fix merged (#10563)
Author Victoria and Rob

Summary

The HAL-7441 parallel-grading work (#10531, merged and deployed to verifier-service at 05:05 UTC) made two Claude Agent SDK control-channel features unconditional in the agentic verifier runner: an in-process SDK MCP render tool and a PreToolUse Bash guard hook. But the prompt was still handed to query() as a plain string. On the string path the SDK calls end_input() the moment it sends the message, closing the CLI's stdin — which is the return leg those two features answer over. With an MCP server configured, the connect reply can never be written back, so the run dies with a CLIConnectionError wrapped in an anyio ExceptionGroup. Every agentic verifier (verify_agent, verify_slides_match_agentic) returned 0.00 for ~14 hours. Worse, is_infra_exception() did not unwrap ExceptionGroup, so the crash was scored as a genuine zero rather than flagged as infrastructure failure and discarded — the outage looked like a wall of clean, legitimate 0s. Fixed by streaming the prompt as a one-message async iterator (#10563), which holds the control channel open for the whole run.

Impact

Timeline (UTC)

When What happened
Aug 4, 05:05 Outage begins. #10531 (HAL-7441) squash-merges to main (7d4c803) and deploys to verifier-service. The render tool + PreToolUse hook go live; every agentic verifier now crashes on the string-prompt path.
05:05 – 19:25 ~14 hours of agentic verifiers returning 0.00, recorded as legitimate zeros. No alert fires.
~afternoon Wyatt notices agentic-graded runs coming back uniformly 0.0 with ExceptionGroup in the logs, and files HAL-7501.
19:10 #10563 opened — stream the prompt via single_user_message, plus unwrap ExceptionGroup in is_infra_exception().
~19:25 Recovery. #10563 merges to main (fdd2f73) and deploys; agentic verifiers connect and grade again.

How it happened

  1. Why did agentic verifiers score 0? The Agent SDK run raised a CLIConnectionError ("Tool permission stream closed before response received"), the verifier caught it, and returned 0.0.
  2. Why did the SDK connection fail? The in-process SDK MCP render server and the PreToolUse hook answer the CLI over a bidirectional control protocol whose return leg is the CLI's stdin. Stdin was already closed.
  3. Why was stdin closed? query() was called with a plain string prompt. On the string path the SDK sends the one message and immediately calls end_input(), closing stdin — fine when nothing needs to talk back, fatal once an MCP server or hook does.
  4. Why did we add MCP-server + hook without switching to streaming input? They were introduced by the HAL-7441 concurrency-hardening work (the render tool to serialize LibreOffice, the Bash guard to block raw soffice) and made unconditional, but the prompt-passing call was never revisited — the string path had always worked because nothing used the control channel before.
  5. Why was it invisible for 14 hours? is_infra_exception() didn't unwrap ExceptionGroup, so a control-channel crash was classified as a normal verifier result and scored 0.0 instead of discarding the episode as broken infrastructure. The failure mode was indistinguishable from a model genuinely earning zero.

Root cause: new SDK control-channel features (MCP server + hook) were made mandatory on an input path (query(prompt=<str>)) that closes the control channel before they can respond — and the grading pipeline scored the resulting crash as a real 0 rather than an infra failure, so nothing surfaced it.

What surprised us

What went well / what went poorly

Went well

Went poorly

Action items

# Action Priority Ticket
1 Stream the agent prompt (single_user_message) so the SDK control channel stays open whenever an MCP server or hook is configured P0 Resolved — #10563 (HAL-7501)
2 Unwrap ExceptionGroup/BaseExceptionGroup in is_infra_exception() so control-channel crashes are discarded as infra failures, not scored 0 P0 Resolved — #10563 (HAL-7501)
3 Post-deploy canary: dispatch one agentic verifier after every verifier-service deploy and assert it connects and scores non-crashing P1 HAL-7503
4 Alert on the silent signature — a rubric returning all-exactly-0.0, or a spike in agentic-verifier ExceptionGroup errors P1 HAL-7504
5 Runner guard: never enable SDK mcp_servers/hooks on a string-prompt query(); encode the streaming requirement so it can't regress P2 HAL-7506

Evidence: verifier-service logs (ExceptionGroup + "Tool permission stream closed before response received"), _agent_runner.py prompt-passing diff across #10531 → #10563, HAL-7441 deploy at 05:05 UTC (7d4c803), HAL-7501 fix at ~19:25 UTC (fdd2f73).