Docs: hooks.json timeout key is documented but runtime requires timeout_sec

Open 💬 1 comment Opened Jul 25, 2026 by zazula

The official Hooks documentation documents timeout as the key for hook command configurations:

{
  "type": "command",
  "command": "python3 ~/.codex/hooks/session_end.py",
  "timeout": 3
}

The docs also state:

timeout is in seconds. If timeout is omitted, Codex uses 600 seconds for most hooks.

However, in practice, the timeout key is not accepted by the runtime. The key that actually works is timeout_sec.

This is a documentation bug -- the runtime field name does not match what is published.

Affected surfaces

  • ~/.codex/hooks.json
  • hooks.json in any .codex/ directory
  • Inline [hooks] tables in config.toml

Expected fix

Either:

  1. Update the docs to document timeout_sec as the correct key, or
  2. Add timeout_sec as an accepted alias if the runtime already supports both.

---

Source Code Evidence

The Codex Rust source confirms timeout is the only accepted JSON key.

File: codex-rs/config/src/hook_config.rs lines 147-150:

#[serde(default, rename = "timeout")]
timeout_sec: Option<u64>,

The rename = "timeout" serde attribute means the JSON field name is timeout. The Rust struct field is named timeout_sec — that's just an internal naming convention, not the JSON key.

This is the complete serde configuration for the timeout field: there is no alias attribute for timeout_sec, so timeout_sec as a JSON key is not accepted by the parser.

Test evidence: The same source includes a test hooks_file_deserializes_existing_json_shape that parses "timeout": 10 and asserts success — confirming this is the tested, supported shape.

Reference: https://learn.chatgpt.com/docs/hooks.md uses "timeout": 3 and "timeout": 30 in all examples, matching the runtime source.

View original on GitHub ↗

1 Comment

Anand-0037 · 1 month ago

The issue currently appears internally inconsistent.

The title and opening section say that timeout is rejected and timeout_sec works, but the source analysis later in the report shows:

#[serde(default, rename = "timeout")]
timeout_sec: Option<u64>,

That indicates timeout is the accepted serialized key, while timeout_sec is only the internal Rust field name.

Could you share the exact runtime reproduction that showed the opposite behavior, including:

  • Codex version
  • configuration surface used (hooks.json or config.toml)
  • the exact configuration
  • the observed error or timeout behavior

Without that reproduction, the current source and existing deserialization test suggest the documentation is already correct and the issue title/opening may need to be updated.

I can also add a focused local test covering both "timeout" and "timeout_sec" if maintainers want additional confirmation.