Linux remote compact can time out at ~31s despite 20m request timeout due to reqwest TCP_USER_TIMEOUT

Resolved 💬 3 comments Opened May 24, 2026 by lzcmian Closed Jun 29, 2026
💡 Likely answer: A maintainer (github-actions[bot], contributor) responded on this thread — see the highlighted reply below.

Summary

On Linux, legacy remote compaction can fail at ~31s per attempt even though Codex configures /responses/compact with a 20 minute request timeout.

I reproduced this on Codex CLI 0.133.0 / source tag rust-v0.133.0 and verified that changing only the reqwest TCP_USER_TIMEOUT setting made the same /compact request succeed.

What was measured

Codex selected the legacy compact path:

remote_compaction_v2=false
provider_name=OpenAI
provider_base_url=https://chatgpt.com/backend-api/codex
endpoint="/responses/compact"
stream_idle_timeout_ms=300000
compact_request_timeout_ms=1200000

Despite the 20 minute compact request timeout, each HTTP attempt timed out at ~31s:

attempt=0 duration_ms=30885 error.message="timeout" endpoint="/responses/compact"
attempt=1 duration_ms=31043 error.message="timeout" endpoint="/responses/compact"
attempt=2 duration_ms=31334 error.message="timeout" endpoint="/responses/compact"
attempt=3 duration_ms=30869 error.message="timeout" endpoint="/responses/compact"
attempt=4 duration_ms=31043 error.message="timeout" endpoint="/responses/compact"

codex_api::endpoint::session: close time.idle=158s
codex_core::compact_remote: remote compaction failed ... compact_error=request timed out

So the visible ~2m37s failure was 5 * ~31s, not Codex's compact request timeout.

Patch tested

I rebuilt Codex with only this reqwest client change:

let tcp_user_timeout = Duration::from_secs(600);
let mut builder = reqwest::Client::builder()
    .default_headers(default_headers())
    .tcp_user_timeout(tcp_user_timeout);

After that change, the same thread and same /compact operation succeeded on the first attempt:

debug reqwest tcp_user_timeout override tcp_user_timeout_ms=600000
debug compact request timeout ... compact_request_timeout_ms=1200000 endpoint="/responses/compact"

event.name="codex.api_request"
duration_ms=42722
http.response.status_code=200
attempt=0
endpoint="/responses/compact"

codex_api::endpoint::session: close time.idle=42.7s

The backend returned successfully after ~42.7s. The unpatched Linux client never waited that long because the lower transport-level timeout fired first.

Likely cause

reqwest 0.12 sets Linux TCP_USER_TIMEOUT to 30 seconds by default. For long-running unary requests such as legacy POST /responses/compact, that can become the effective lower timeout, even when Codex sets a much longer per-request timeout.

This specific reproduction is Linux-only. In reqwest 0.12.28, both the public tcp_user_timeout(...) builder method and the default tcp_user_timeout: Some(Duration::from_secs(30)) are compiled only for:

#[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]

So macOS developers are unlikely to reproduce this exact ~31s per attempt failure mode. They may still see other remote compaction failures, but not this specific Linux reqwest TCP_USER_TIMEOUT=30s cause.

Suggested fix

Ensure the reqwest TCP_USER_TIMEOUT used by Codex HTTP traffic is not shorter than the request timeout for long-running unary endpoints such as /responses/compact.

Possible approaches:

  1. Disable TCP_USER_TIMEOUT for Codex's normal HTTP client.
  2. Set it to a larger value.
  3. Override it for legacy /responses/compact.
  4. Move affected OpenAI traffic to a streamed compaction path where this unary idle request shape no longer exists.

Related:

  • #11282 (/compact SSE keepalive approach for long-running compactions)
  • #18450
  • #22798
  • #23451
  • #23697 (remote_compaction_v2 for OpenAI, likely avoids the legacy unary path)

View original on GitHub ↗

3 Comments

github-actions[bot] contributor · 3 months ago

Potential duplicates detected. Please review them and close your issue if it is a duplicate.

  • #22798

Powered by Codex Action

zaid-marji · 3 months ago

Independent reproduction on a separate Linux setup confirms this. Same symptom (~31s per attempt, 5 × ~31s ≈ 153s total to user-visible failure), same os error 110 (ETIMEDOUT) source chain inside hyper, same socket-level cause.

Two small additions that may be useful:

Choosing the override value. Tying TCP_USER_TIMEOUT to the existing compact_request_timeout = stream_idle_timeout × COMPACT_REQUEST_TIMEOUT_IDLE_MULTIPLIER (currently 1200000 ms) keeps the two layers self-consistent — the socket option exactly matches the longest .timeout() any unary caller sets, so it can never silently shadow the application budget. A new contributor reading either layer in isolation will arrive at the same number. Picking an unrelated value (e.g. 600 s) is fine functionally but creates an invariant the codebase doesn't enforce.

Why a static override is enough vs. per-endpoint scoping. Approach 3 from the original ("override for /responses/compact only") would require threading through EndpointSession so individual endpoints can request a different socket option, which reqwest doesn't really model — Client is the granularity. Setting it once on the shared client (capped at 1200 s) keeps healthy connections unaffected, only changes behavior for sockets that previously failed at 30 s.

If the team would like a ready-to-pick branch, I have one rebased on current main (cfg-gated to Linux/Android/Fuchsia, cargo check + cargo clippy clean, verified end-to-end against chatgpt.com/backend-api/codex/responses/compact with a ~226 K-token context that previously failed):

https://github.com/zaid-marji/codex/tree/fix/tcp-user-timeout-compact

Happy to expand or wait for direction per the invite-only PR process.

etraut-openai contributor · 1 month ago

Thanks for reporting this problem. Until recently, Codex used a separate endpoint and server-side logic for compaction. We recently switched to a more robust approach where the compaction logic is moved locally using the same endpoint as normal turns. This eliminates "remote compaction" errors and increases the stability and reliability of compaction operations. If you see further problems with compaction, please use /feedback and open a new bug report.