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
- 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.
- Ensure nothing else writes or locks the output directory during export (parallel just targets, file watchers).
- Check permissions on the out_dir tree.
- 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
- Never run two exports into the same output directory concurrently.
- Keep RUST_BACKTRACE=1 set in CI for export targets so swallowed panics remain visible.
- Treat this error as a wrapper: the actionable stack trace is on stderr from the panicked worker, not in the error itself.
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
- Prettier failed with status {status}
- expected bundle root to be an object
- expected bundle definitions map
- expected v2 namespace in bundle definitions
- current-time request failed: code={} message={}
AI-assisted analysis of openai/codex@339751715c (2026-08-25).
Data as JSON: /api/errors/227de8d715752939.
Report an issue: GitHub.