Skip to content

fix(agent_toolset): reject non-regular files in the write tool instead of hanging on a FIFO#1752

Open
roli-lpci wants to merge 1 commit into
anthropics:mainfrom
roli-lpci:fix/write-tool-fifo-guard
Open

fix(agent_toolset): reject non-regular files in the write tool instead of hanging on a FIFO#1752
roli-lpci wants to merge 1 commit into
anthropics:mainfrom
roli-lpci:fix/write-tool-fifo-guard

Conversation

@roli-lpci

Copy link
Copy Markdown

What

beta_write_tool in src/anthropic/lib/tools/agent_toolset.py has no guard against the target path being a
FIFO (or other non-regular file). beta_read_tool and beta_edit_tool both stat() the target and reject
anything that isn't S_ISREG before ever calling open(), specifically because opening an unconnected FIFO for
I/O blocks the calling thread indefinitely. beta_edit_tool's guard is a direct copy of beta_read_tool's
("Same guard as the read tool, edit reads the whole file too."), but beta_write_tool never received it.

Since write()'s file I/O runs directly in the coroutine (not offloaded to a worker thread), calling write
on a path that resolves to a FIFO hangs the call forever and freezes the whole async event loop with it, not
just the one tool invocation. A directory at the same path does not trigger this: write_text() on a
directory raises IsADirectoryError immediately, which the existing except OSError already converts to a
clean ToolError. A FIFO is the case that actually blocks instead of raising, and it's reachable any time an
agent using this reference toolset is asked to write to a path that happens to be a named pipe, for example
one it created itself moments earlier with the bash tool (mkfifo), or one already present in a shared /
self-hosted environment.

Fix

stat() the target before opening it, same as read/edit, and reject non-regular files with a ToolError.
Unlike read/edit, write's target commonly does not exist yet (a normal new-file write), so a missing file
is explicitly not an error here: only an existing non-regular file is rejected.

try:
    st: os.stat_result | None = target.stat()
except FileNotFoundError:
    st = None
if st is not None and not S_ISREG(st.st_mode):
    raise ToolError(f"write: {file_path}: not a regular file")

12 lines changed in agent_toolset.py, all inside beta_write_tool. No signature or return-type changes.

Test

Added test_write_rejects_fifo_instead_of_blocking to tests/lib/tools/test_agent_toolset.py, mirroring the
existing test_read_rejects_directory / test_edit_rejects_directory pattern but using a real os.mkfifo FIFO
(POSIX-only, skipped on Windows) since a directory doesn't reproduce this specific failure mode (it already
fails fast via IsADirectoryError, only a FIFO blocks). The call runs on a background thread with a bounded
join() so an unfixed SDK fails the test in ~5s instead of hanging the run:

  • Before this fix: the test fails with "write() hung opening a FIFO instead of rejecting it as a non-regular file" after a 5s bounded wait.
  • After this fix: the test passes in <0.1s, asserting a ToolError containing "not a regular file".

Full mapped test file (tests/lib/tools/test_agent_toolset.py): 41 passed with the fix (the new test fails on
the unmodified baseline as above; the other 40 pass on both). ruff check and ruff format --check are clean
on both changed files.

Scope

This only touches beta_write_tool. It does not change beta_read_tool or beta_edit_tool, and it does not
move the file I/O off the event loop thread (a separate, larger change if the maintainers want it); this PR is
scoped to closing the specific gap where write is the only one of the three file-mutating tools without the
non-regular-file guard the reference toolset's own docstring says the file tools provide ("the file tools ...
are safe to use without a sandbox").

Happy to extend the test to also cover a character device (e.g. skip on CI sandboxes without /dev/null
writable) if useful, but the FIFO case alone is a deterministic, cross-CI-safe repro of the hang.

…d of hanging on a FIFO

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@roli-lpci roli-lpci requested a review from a team as a code owner July 11, 2026 16:44
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant