Hook command runner buffers unbounded stdout/stderr in memory
Nobody has claimed this yet.
Assessment
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Newbie friendliness
- 52/100
Research direction
Start in codex-rs/hooks/src/engine/command_runner.rs around lines 64-111 and reproduce the issue with the provided spam_hook.py and hook configuration while observing RSS. Define the runtime output limits with maintainers, then verify bounded stdout and stderr, malformed UTF-8 handling, concurrent noisy handlers, and cleanup using the listed acceptance tests.
Written by the indexing model from the issue text.
Description
What variant of Codex are you using?
Codex CLI/TUI hooks on current main (49025589b0216b876b1a6a20977536c7d55cdb8b). The same hook engine is shared by other surfaces.
What issue are you seeing?
The command-hook runner buffers the complete stdout and stderr streams in memory until the handler exits or times out. There is no byte or character limit on either stream.
Current code pipes both streams and awaits child.wait_with_output(), then creates lossy UTF-8 String copies:
A noisy, broken, or plugin-provided hook can therefore make Codex memory usage scale with the amount of output produced before process exit. Concurrent hook handlers amplify the peak. The configured timeout bounds elapsed time, but it does not bound bytes produced during that interval.
Minimal reproduction
Register a command hook whose script writes a configurable amount of data:
# spam_hook.py
import os
import sys
mib = int(os.environ.get("HOOK_OUTPUT_MIB", "64"))
chunk = b"x" * (1024 * 1024)
for _ in range(mib):
sys.stdout.buffer.write(chunk)
{
"hooks": {
"UserPromptSubmit": [{
"hooks": [{
"type": "command",
"command": "python spam_hook.py",
"timeout": 30
}]
}]
}
}
Trigger the event while observing Codex RSS. Increasing HOOK_OUTPUT_MIB increases retained output proportionally. The payload is invalid hook JSON, but it is not parsed or rejected until after the complete stream has been accumulated.
The same behavior applies to stderr, and String::from_utf8_lossy(...).to_string() can add another allocation after collection.
Expected behavior
Hook process output should have a runtime-owned hard bound independent of handler timeout and additionalContextLimit.
A safe contract would:
- read stdout and stderr concurrently with fixed byte/character ceilings;
- terminate the handler when stdout exceeds the protocol limit;
- drain stderr without retaining it indefinitely, keeping only a small bounded diagnostic tail if needed;
- reject malformed UTF-8 rather than silently normalizing protocol bytes;
- report a bounded, content-free failure reason;
- ensure overflow/timeout cleanup terminates the relevant process tree.
Suggested acceptance tests
- a handler producing output just below the limit succeeds;
- output above the limit is terminated and reported without retaining the full stream;
- unbounded stderr does not cause unbounded RSS growth;
- malformed UTF-8 is rejected deterministically;
- several concurrent noisy handlers remain within an aggregate memory bound;
- overflow and timeout leave no surviving child process holding the pipes open.
Related but distinct: #4337 covers process-tree cleanup for shell tool calls, and #21233 reports general Codex memory growth. I could not find an existing issue for the hook command runner's unbounded stdout/stderr capture.
I can prepare a focused implementation and regression tests if maintainers confirm the desired limits and invite a PR.
- Dominant language
- Rust
- Stars
- 125k
- Forks
- 19.5k
- Avg merge
- 1m
- Merged PRs (30d)
- 1k
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
More from openai/codex
-
enhancement remote
Difficulty 2/5 1-3 hours Newbie friendliness 68/100
-
bug CLI windows-os
Difficulty 2/5 1-3 hours Newbie friendliness 76/100
-
macOS sandbox blocks hw.optional.arm64 sysctl, causing Flutter to misdetect Apple Silicon as x64 Openbug CLI sandbox
Difficulty 2/5 1-3 hours Newbie friendliness 78/100
-
bug CLI TUI
Difficulty 2/5 1-3 hours Newbie friendliness 90/100
-
CLI config enhancement skills
Difficulty 2/5 1-3 hours Newbie friendliness 84/100
Similar issues
-
Difficulty 2/5 1-3 hours Newbie friendliness 86/100
kwakseongjae/auto-hwp#319 ·
-
area:cli bug filter-quality good first issue priority:medium
Difficulty 2/5 1-3 hours Newbie friendliness 84/100
-
Difficulty 1/5 Under an hour Newbie friendliness 72/100
bevyengine/bevy#25861 ·
-
comp-datalake
Difficulty 2/5 1-3 hours Newbie friendliness 88/100
ClickHouse/ClickHouse#121222 ·
-
A-linter
Difficulty 2/5 1-3 hours Newbie friendliness 72/100
oxc-project/oxc#26863 ·