openai/codex · error · anyhow::Error

TypeScript header worker panicked

Error message

TypeScript header worker panicked

What it means

generate_ts_with_options prepends the GENERATED CODE header to every emitted .ts file using scoped worker threads. worker.join() returning Err means a worker panicked mid-chunk, and the panic payload is discarded in favor of this anyhow error, so the original panic message only reaches stderr. The root cause is whatever made the header-prepend path panic, typically an I/O failure on a file in the output tree.

Source

Thrown at codex-rs/app-server-protocol/src/export.rs:162

        let worker_count = thread::available_parallelism()
            .map_or(1, usize::from)
            .min(ts_files.len().max(1));
        let chunk_size = ts_files.len().div_ceil(worker_count);
        thread::scope(|scope| -> Result<()> {
            let mut workers = Vec::new();
            for chunk in ts_files.chunks(chunk_size.max(1)) {
                workers.push(scope.spawn(move || -> Result<()> {
                    for file in chunk {
                        prepend_header_if_missing(file)?;
                    }
                    Ok(())
                }));
            }

            for worker in workers {
                worker
                    .join()
                    .map_err(|_| anyhow!("TypeScript header worker panicked"))??;
            }

            Ok(())
        })?;
    }

    // Optionally run Prettier on all generated TS files.
    if options.run_prettier
        && let Some(prettier_bin) = prettier
        && !ts_files.is_empty()
    {
        let status = Command::new(prettier_bin)
            .arg("--write")
            .arg("--log-level")
            .arg("warn")
            .args(ts_files.iter().map(|p| p.as_os_str()))
            .status()
            .with_context(|| format!("Failed to invoke Prettier at {}", prettier_bin.display()))?;

View on GitHub (pinned to 339751715c)

Solutions

  1. Rerun with RUST_BACKTRACE=1: the worker's panic message and location still print to stderr even though join drops the payload; that names the failing line.
  2. Ensure nothing else writes or locks the output directory during export (parallel just targets, file watchers).
  3. Check permissions on the out_dir tree.
  4. If the panic points inside the header-prepend logic, fix the unwrap or expect there; this error is only a wrapper.

Example fix

// before
worker.join().map_err(|_| anyhow!("TypeScript header worker panicked"))??;

// after - keep the panic message instead of discarding it
worker.join().map_err(|payload| {
    let msg = payload
        .downcast_ref::<&str>()
        .map(|s| (*s).to_string())
        .or_else(|| payload.downcast_ref::<String>().cloned())
        .unwrap_or_else(|| "unknown panic".to_string());
    anyhow!("TypeScript header worker panicked: {msg}")
})??;
Defensive patterns

Strategy: try-catch

Validate before calling

// Verify the output tree is writable before export:
let probe = out_dir.join(".write-probe");
std::fs::write(&probe, b"")?;
std::fs::remove_file(&probe)?;

Try / catch

if let Err(err) = generate_ts_with_options(&out_dir, prettier.as_deref(), opts) {
    if err.to_string().contains("header worker panicked") {
        // Re-run once, serially, with RUST_BACKTRACE=1 to capture the worker
        // panic; concurrent writers on out_dir are the usual culprit.
    }
}

Prevention

When it happens

Trigger: Running the TS export with ensure_headers enabled while a worker panics: filesystem errors unwrapped inside the header-prepend path, files concurrently removed or locked by another process, or an invariant violation introduced by a refactor of the prepend logic.

Common situations: Two export processes writing the same out_dir at once; editors, watchers, or antivirus locking generated files; a code change that turned a handled error in the prepend path into an unwrap.

Related errors


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