{"id":"598d092e95589cd5","repo":"rust-lang/cargo","slug":"artifact-dir-was-not-locked-598d09","errorCode":null,"errorMessage":"artifact-dir was not locked","messagePattern":"artifact-dir was not locked","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/compiler/timings/mod.rs","lineNumber":271,"sourceCode":"        *prev = current;\n        self.last_cpu_recording = now;\n        let dur = now.duration_since(self.start).as_secs_f64();\n        self.cpu_usage.push((dur, 100.0 - pct_idle));\n    }\n\n    /// Call this when all units are finished.\n    pub fn finished(\n        &mut self,\n        build_runner: &BuildRunner<'_, '_>,\n        error: &Option<anyhow::Error>,\n    ) -> CargoResult<()> {\n        if let Some(logger) = build_runner.bcx.logger\n            && let Some(logs) = logger.get_logs()\n        {\n            let timings_path = build_runner\n                .files()\n                .timings_dir()\n                .expect(\"artifact-dir was not locked\");\n            paths::create_dir_all(&timings_path)?;\n            let run_id = logger.run_id();\n            let filename = timings_path.join(format!(\"cargo-timing-{run_id}.html\"));\n            let mut f = BufWriter::new(paths::create(&filename)?);\n\n            let mut ctx = prepare_context(logs.into_iter(), run_id, false)?;\n            ctx.error = error;\n            ctx.cpu_usage = &self.cpu_usage;\n            report::write_html(ctx, &mut f)?;\n\n            let unstamped_filename = timings_path.join(\"cargo-timing.html\");\n            paths::link_or_copy(&filename, &unstamped_filename)?;\n\n            let mut shell = self.gctx.shell();\n            let timing_path = std::env::current_dir().unwrap_or_default().join(&filename);\n            let link = shell.err_file_hyperlink(&timing_path);\n            let msg = format!(\"report saved to {link}{}{link:#}\", timing_path.display(),);\n            shell.status_with_color(\"Timing\", msg, &style::NOTE)?;","sourceCodeStart":253,"sourceCodeEnd":289,"githubUrl":"https://github.com/rust-lang/cargo/blob/0e07a155371a6ce88ae53a2c00df940280c09a67/src/compiler/timings/mod.rs#L253-L289","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Ensure the build runner calls layout locking (prepare/lock the artifact-dir) before invoking compilation when timings or logging is enabled.","If using cargo as a library, verify your BuildRunner configuration mirrors what cargo init does for artifact-dir setup.","Clear the target directory (cargo clean) and rebuild to regenerate fresh layout metadata.","Update cargo to the latest stable release — if this is an internal regression it will have been fixed."],"exampleFix":"// before\nlet timings_path = build_runner.files().timings_dir().expect(\"artifact-dir was not locked\");\n// after — fall back gracefully instead of panicking\nlet Some(timings_path) = build_runner.files().timings_dir() else {\n    tracing::warn!(\"timings requested but artifact-dir was not locked; skipping report\");\n    return Ok(());\n};","handlingStrategy":"validation","validationCode":"// Before calling build_runner timings, verify artifact-dir is available\nif build_runner.files().timings_dir().is_none() {\n    eprintln!(\"warning: timings requested but artifact-dir not locked; skipping\");\n} else {\n    timings.finished(build_runner, &error)?;\n}","typeGuard":null,"tryCatchPattern":"// For embedded library users: catch the panic\nuse std::panic;\nlet result = panic::catch_unwind(|| {\n    timings.finished(build_runner, &error)\n});\nif result.is_err() {\n    eprintln!(\"timings report generation panicked; continuing\");\n}","preventionTips":["Ensure the build runner establishes artifact-dir layout locking before compilation when timings/logging is enabled.","Use cargo clean to reset stale layout metadata before builds with --timings.","Keep cargo updated — internal layout invariants are refined across releases."],"tags":["rust","cargo","panic","invariant","timings","build-layout"],"analyzedSha":"0e07a155371a6ce88ae53a2c00df940280c09a67","analyzedAt":"2026-08-06T01:46:58.334Z","schemaVersion":2}