openai/codex · warning · anyhow::Error

Snapshot command timed out for {shell_name}

Error message

Snapshot command timed out for {shell_name}

What it means

run_script_with_timeout wraps the snapshot shell process in tokio::time::timeout with SNAPSHOT_TIMEOUT = 10 seconds (shell_snapshot.rs:45), with kill_on_drop(true) so the child is reaped when the future is dropped. The error fires when sourcing rcfiles plus emitting the snapshot exceeds those 10s. Both snapshot creation and snapshot validation (which re-sources the written file) run through this budget.

Source

Thrown at codex-rs/core/src/shell_snapshot.rs:302

    // Handler is kept as guard to control the drop. The `mut` pattern is required because .args()
    // returns a ref of handler.
    let mut handler = Command::new(&args[0]);
    codex_protocol::shell_environment::scrub_non_inheritable_env_vars(handler.as_std_mut());
    handler.args(&args[1..]);
    handler.stdin(Stdio::null());
    handler.current_dir(cwd);
    #[cfg(unix)]
    unsafe {
        handler.pre_exec(|| {
            codex_utils_pty::process_group::detach_from_tty()?;
            Ok(())
        });
    }
    handler.kill_on_drop(true);
    let output = timeout(snapshot_timeout, handler.output())
        .await
        .map_err(|_| anyhow!("Snapshot command timed out for {shell_name}"))?
        .with_context(|| format!("Failed to execute {shell_name}"))?;

    if !output.status.success() {
        let status = output.status;
        let stderr = String::from_utf8_lossy(&output.stderr);
        bail!("Snapshot command exited with status {status}: {stderr}");
    }

    Ok(String::from_utf8_lossy(&output.stdout).into_owned())
}

/// Removes shell snapshots that either lack a matching session rollout file or
/// whose rollouts have not been updated within the retention window.
/// The active session id is exempt from cleanup.
pub async fn cleanup_stale_snapshots(
    codex_home: &AbsolutePathBuf,
    active_session_id: ThreadId,
    state_db: Option<StateDbHandle>,

View on GitHub (pinned to 339751715c)

Solutions

  1. Time your shell startup (zsh -i -c 'exit' with a prompt timer, or time zsh -lc true) and trim/lazy-load the slow rcfile parts (defer nvm, conda hooks)
  2. Move network-touching init out of rcfiles or cache their results
  3. Skip heavy init when non-interactive so snapshot runs stay lean
  4. If startup legitimately needs more, disable shell snapshots rather than fighting the fixed 10s budget (the timeout constant is not user-configurable)

Example fix

# ~/.zshrc — before
source ~/.nvm/nvm.sh              # seconds of startup cost

# after — lazy-load so snapshot runs stay fast
if [[ -n "$USE_NVM" ]]; then source ~/.nvm/nvm.sh; else
  nvm() { unfunction nvm; source ~/.nvm/nvm.sh; nvm "$@"; }
fi
Defensive patterns

Strategy: fallback

Validate before calling

// Measure login-shell startup before enabling snapshots
// e.g. in shell: TIMEFMT='%E'; time zsh -lc true  — keep it well under the 10s budget

Try / catch

match try_create(...).await {
    Err("write_failed") | Err("validation_failed") => {
        warn_and_continue_without_snapshot(); }
    ok => ok,
}

Prevention

When it happens

Trigger: Login-shell (-lc) snapshot run where rcfiles take >10s: nvm/pyenv/conda/rbenv init, network calls (version managers, proxy checks, prompt themes fetching data), sleeps, or huge shell histories; also a validation run re-sourcing an enormous generated snapshot.

Common situations: Heavy developer dotfiles (nvm.sh alone can take seconds), corporate login scripts, DNS/proxy stalls inside rcfiles, resource-starved CI or containers making shell startup crawl.

Understand the failure class

Related errors


AI-assisted analysis of openai/codex@339751715c (2026-08-25). Data as JSON: /api/errors/e806e7133bb852ca. Report an issue: GitHub.