Sandbox blocks asyncio self-pipe wakeups: call_soon_threadsafe/to_thread/anyio hang with PermissionError (EPERM)
What version of Codex CLI is running?
codex-cli 0.115.0
What subscription do you have?
Team
Which model were you using?
gpt-5.4-medium,gpt-5.4-xhigh
What platform is your computer?
Linux 6.6.87.2-microsoft-standard-WSL2 x86_64 unknown
What terminal emulator and version are you using (if applicable)?
Google Antigravity
What issue are you seeing?
### Summary
I found what looks like a Codex sandbox issue.
Inside the Codex sandbox, Python async code that relies on cross-thread event loop wakeups
can hang or time out. This affects:
asyncio.call_soon_threadsafe(...)asyncio.to_thread(...)loop.run_in_executor(None, ...)anyio.to_thread.run_sync(...)anyio.Path(...).exists()and similar helpers that offload sync work to threads
Outside the sandbox, the same commands complete successfully.
### Environment
- Codex CLI sandbox
- Reproduced on March 18, 2026
- Project Python:
.venv/bin/python3 - Python version:
3.13 pytest:9.0.2pytest-asyncio:1.3.0anyio:4.12.1
What steps can reproduce the bug?
### Reproduction
I used this command in a repo only as a probe harness:
``bash``
.venv/bin/python3 scripts/probe_async_executor.py --timeout 2
probe_async_executor.py
Inside the sandbox, the probe showed this pattern:
- asyncio.to_thread(...): timeout
- loop.run_in_executor(None, ...): timeout
- anyio.to_thread.run_sync(...): timeout
- anyio.Path(...).exists(): timeout
- custom ThreadPoolExecutor(...) cases: success
Outside the sandbox, the exact same command completed successfully and all probe cases
passed.
### Minimal repro
This hangs inside the sandbox:
```
import asyncio
import threading
def cb(loop):
print("thread:call_soon_threadsafe", flush=True)
loop.call_soon_threadsafe(lambda: (print("loop:callback", flush=True), loop.stop()))
loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop)
threading.Timer(0.5, cb, args=(loop,)).start()
loop.run_forever()
print("done", flush=True)
loop.close()
Expected output:
thread:call_soon_threadsafe
loop:callback
done
```
Actual behavior inside the sandbox:
- only thread:call_soon_threadsafe prints
- the event loop never wakes up
- the process hangs until killed / times out
### Root cause evidence
With PYTHONASYNCIODEBUG=1, asyncio logs the real failure:
DEBUG:asyncio:Fail to write a null byte into the self-pipe socket
Traceback (most recent call last):
File "/usr/lib/python3.13/asyncio/selector_events.py", line 152, in _write_to_self
csock.send(b'\0')
PermissionError: [Errno 1] Operation not permitted
This appears to break the Unix selector event loop wakeup mechanism used by
call_soon_threadsafe().
Relevant stdlib path:
- asyncio/selector_events.py, _write_to_self() uses csock.send(b'\0')
- asyncio/base_events.py, shutdown_default_executor() depends on call_soon_threadsafe(...)
That explains why some asyncio.to_thread(...) work appears to finish but asyncio.run(...)
still hangs during shutdown of the default executor.
### Extra confirmation
I also verified that this is not specific to the repo or virtualenv startup hooks:
- reproduces with plain python3
- reproduces with python3 -S
- does not require importing any project code
- disappears when rerun outside the Codex sandbox
I also tested lower-level primitives:
- cross-thread socket.socketpair().send(...) fails with PermissionError: [Errno 1] Operation
not permitted
- cross-thread os.pipe() write works
So this looks specifically related to the sandbox blocking the socketpair-based wakeup path
used by asyncio on Unix.
What is the expected behavior?
### Expected behavior
Codex sandboxed Python processes should be able to use standard-library asyncio thread
wakeups correctly, or the sandbox should detect and clearly report that this runtime pattern
is unsupported.
### Actual behavior
The sandbox causes false async failures that look like application bugs:
- hangs in asyncio.to_thread(...)
- hangs in anyio.to_thread.run_sync(...)
- hangs during shutdown_default_executor()
- misleading timeouts in pytest and repo diagnostics
Additional information
_No response_
This issue has 1 comment on GitHub. Read the full discussion on GitHub ↗