rust-lang/cargo · error

artifact-dir was not locked

Error message

artifact-dir was not locked

What it means

This panic fires in Timings::finished() when cargo tries to write the HTML timing report at the end of a build. The timings output directory lives under the artifact-dir layout (timings_dir() delegates to host.artifact_dir().map(|v| v.timings())), which is only populated when the artifact-dir has been locked during the build. If a logger is present (timings or logging enabled) but the artifact-dir layout is None, the .expect() aborts the process.

Source

Thrown at src/compiler/timings/mod.rs:271

        *prev = current;
        self.last_cpu_recording = now;
        let dur = now.duration_since(self.start).as_secs_f64();
        self.cpu_usage.push((dur, 100.0 - pct_idle));
    }

    /// Call this when all units are finished.
    pub fn finished(
        &mut self,
        build_runner: &BuildRunner<'_, '_>,
        error: &Option<anyhow::Error>,
    ) -> CargoResult<()> {
        if let Some(logger) = build_runner.bcx.logger
            && let Some(logs) = logger.get_logs()
        {
            let timings_path = build_runner
                .files()
                .timings_dir()
                .expect("artifact-dir was not locked");
            paths::create_dir_all(&timings_path)?;
            let run_id = logger.run_id();
            let filename = timings_path.join(format!("cargo-timing-{run_id}.html"));
            let mut f = BufWriter::new(paths::create(&filename)?);

            let mut ctx = prepare_context(logs.into_iter(), run_id, false)?;
            ctx.error = error;
            ctx.cpu_usage = &self.cpu_usage;
            report::write_html(ctx, &mut f)?;

            let unstamped_filename = timings_path.join("cargo-timing.html");
            paths::link_or_copy(&filename, &unstamped_filename)?;

            let mut shell = self.gctx.shell();
            let timing_path = std::env::current_dir().unwrap_or_default().join(&filename);
            let link = shell.err_file_hyperlink(&timing_path);
            let msg = format!("report saved to {link}{}{link:#}", timing_path.display(),);
            shell.status_with_color("Timing", msg, &style::NOTE)?;

View on GitHub (pinned to 0e07a15537)

Solutions

  1. Ensure the build runner calls layout locking (prepare/lock the artifact-dir) before invoking compilation when timings or logging is enabled.
  2. If using cargo as a library, verify your BuildRunner configuration mirrors what cargo init does for artifact-dir setup.
  3. Clear the target directory (cargo clean) and rebuild to regenerate fresh layout metadata.
  4. Update cargo to the latest stable release — if this is an internal regression it will have been fixed.

Example fix

// before
let timings_path = build_runner.files().timings_dir().expect("artifact-dir was not locked");
// after — fall back gracefully instead of panicking
let Some(timings_path) = build_runner.files().timings_dir() else {
    tracing::warn!("timings requested but artifact-dir was not locked; skipping report");
    return Ok(());
};
Defensive patterns

Strategy: validation

Validate before calling

// Before calling build_runner timings, verify artifact-dir is available
if build_runner.files().timings_dir().is_none() {
    eprintln!("warning: timings requested but artifact-dir not locked; skipping");
} else {
    timings.finished(build_runner, &error)?;
}

Try / catch

// For embedded library users: catch the panic
use std::panic;
let result = panic::catch_unwind(|| {
    timings.finished(build_runner, &error)
});
if result.is_err() {
    eprintln!("timings report generation panicked; continuing");
}

Prevention

When it happens

Trigger: Building with --timings (or [build] timinings = true in config) in a context where the artifact-dir was never locked — e.g., a build runner that was set up without locking the artifact output directory, or a corrupted/interrupted build state where the layout's artifact_dir field is None despite a logger being active.

Common situations: Using cargo as an embedded library with a custom BuildRunner that enables logging but doesn't establish the artifact-dir lock; cargo internals regression after a refactor of layout locking; running --timings against a target directory whose layout metadata is stale or manually deleted.

Related errors


AI-assisted analysis of rust-lang/cargo@0e07a15537 (2026-08-06). Data as JSON: /data/errors/598d092e95589cd5.json. Report an issue: GitHub.