Commit dadb67f
A sandboxed child that dies without sending names the tool it was running
`poll()` returns true when the pipe is readable, and a closed pipe is
readable. So a child that died without sending — `os._exit`, a segfault, an
OOM-kill, anything that kills the process rather than raising inside
`spec.fn` — fell through the timeout guard into `recv()` and raised a bare
`EOFError('')`.
`run` has two failure shapes and that was neither: `SandboxViolation` for a
confinement breach, `RuntimeError(f"tool {name!r} failed: ...")` for the tool's
own exception. An EOFError with an empty message is attributable to nothing.
Downstream is where it bit. `AgentNode`'s loop catches it under its blanket
`except Exception` and renders `f"TOOL_ERROR: {exc}"` — and `str(EOFError(''))`
is `''`, so the model was handed `TOOL_ERROR:` with nothing after the colon. It
was told its call failed and given no way to tell why, which tool, or whether a
retry could help; the same text went into the trace, so the audit trail could
not explain the failure either. The `_CALL_SHAPE_ERROR` hint below that clause
cannot fire on it, since it matches on message text and there is none.
This is the shape of a defect already closed here once: a curtailed phase
writing `[budget_exhausted] ` with nothing after it. Same empty message, one
layer down.
Now a `RuntimeError` naming the tool and the exit code, with a negative one
rendered as the signal that killed it — which is what tells an OOM-kill apart
from a deliberate `_exit`. Deliberately not a `SandboxViolation`: a child dying
is not evidence it tried to escape confinement, and a violation is a specific
accusation that lands in the trace as one.
The three new tests go red without the fix, with the EOFError raising out of
`multiprocessing/connection.py` exactly as reported. The normal return, the
tool-raises and the timeout paths are untouched.
Closes #111
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>1 parent 533001f commit dadb67f
2 files changed
Lines changed: 93 additions & 1 deletion
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
653 | 653 | | |
654 | 654 | | |
655 | 655 | | |
656 | | - | |
| 656 | + | |
| 657 | + | |
| 658 | + | |
| 659 | + | |
| 660 | + | |
| 661 | + | |
| 662 | + | |
| 663 | + | |
| 664 | + | |
| 665 | + | |
| 666 | + | |
| 667 | + | |
| 668 | + | |
| 669 | + | |
| 670 | + | |
| 671 | + | |
| 672 | + | |
| 673 | + | |
| 674 | + | |
| 675 | + | |
| 676 | + | |
| 677 | + | |
| 678 | + | |
| 679 | + | |
| 680 | + | |
| 681 | + | |
657 | 682 | | |
658 | 683 | | |
659 | 684 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
14 | 14 | | |
15 | 15 | | |
16 | 16 | | |
| 17 | + | |
17 | 18 | | |
18 | 19 | | |
19 | 20 | | |
| |||
40 | 41 | | |
41 | 42 | | |
42 | 43 | | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
43 | 53 | | |
44 | 54 | | |
45 | 55 | | |
| |||
206 | 216 | | |
207 | 217 | | |
208 | 218 | | |
| 219 | + | |
| 220 | + | |
| 221 | + | |
| 222 | + | |
| 223 | + | |
| 224 | + | |
| 225 | + | |
| 226 | + | |
| 227 | + | |
| 228 | + | |
| 229 | + | |
| 230 | + | |
| 231 | + | |
| 232 | + | |
| 233 | + | |
| 234 | + | |
| 235 | + | |
| 236 | + | |
| 237 | + | |
| 238 | + | |
| 239 | + | |
| 240 | + | |
| 241 | + | |
| 242 | + | |
| 243 | + | |
| 244 | + | |
| 245 | + | |
| 246 | + | |
| 247 | + | |
| 248 | + | |
| 249 | + | |
| 250 | + | |
| 251 | + | |
| 252 | + | |
| 253 | + | |
| 254 | + | |
| 255 | + | |
| 256 | + | |
| 257 | + | |
| 258 | + | |
| 259 | + | |
| 260 | + | |
| 261 | + | |
| 262 | + | |
| 263 | + | |
| 264 | + | |
| 265 | + | |
| 266 | + | |
| 267 | + | |
| 268 | + | |
| 269 | + | |
| 270 | + | |
| 271 | + | |
| 272 | + | |
| 273 | + | |
| 274 | + | |
| 275 | + | |
209 | 276 | | |
210 | 277 | | |
211 | 278 | | |
| |||
0 commit comments