{"record":{"id":"527b39c7e83c89db","repo":"jdx/mise","slug":"lockfile-symlink-changed-during-preparation-retry-the","errorCode":null,"errorMessage":"lockfile symlink {} changed during preparation; retry the command","messagePattern":"lockfile symlink (.+?) changed during preparation; retry the command","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/lockfile.rs","lineNumber":1975,"sourceCode":"\npub(crate) struct GraphCleanup(graph::SidecarWrites);\nimpl GraphCleanup {\n    pub(crate) fn prune(self) -> Result<()> {\n        self.0.prune()\n    }\n}\nimpl PreparedWrite {\n    pub(crate) fn mutation_paths(&self) -> impl Iterator<Item = &PathBuf> {\n        self.sidecars.files.iter().map(|(path, _)| path)\n    }\n    pub(crate) fn publish_deferred(self) -> Result<GraphCleanup> {\n        // Detect a deployment switch during preparation before publishing any\n        // sidecars. This is a recheck, not a lock against external symlink edits.\n        if let Some(path) = &self.source_symlink\n            && (!path.is_symlink()\n                || !fs::canonicalize(path).is_ok_and(|target| target == self.target))\n        {\n            bail!(\n                \"lockfile symlink {} changed during preparation; retry the command\",\n                display_path(path)\n            );\n        }\n        self.sidecars.publish_files()?;\n        if let Some(tmp) = self.tmp {\n            persist_lockfile_tmp(tmp, &self.target)?;\n        }\n        invalidate_caches();\n        Ok(GraphCleanup(self.sidecars))\n    }\n    pub(crate) fn publish(self) -> Result<()> {\n        self.publish_deferred()?.prune()\n    }\n}\n\n/// Determines the lockfile path for a given config file path\n/// Returns (lockfile_path, is_local)","sourceCodeStart":1957,"sourceCodeEnd":1993,"githubUrl":"https://github.com/jdx/mise/blob/533346cc374382b41ec5ff70536252b2e96e725c/src/lockfile.rs#L1957-L1993","documentation":"mise's lockfile generation resolves tools against the target of the `source_symlink` (e.g. a versioned lockfile symlink pointing at the active lockfile deployment). Before publishing sidecar files it rechecks that the symlink still points to the same canonical target it saw when preparation started. If the symlink was removed, replaced, or re-pointed mid-run, it aborts before publishing anything so a stale or foreign lockfile is not augmented, and asks the user to rerun.","triggerScenarios":"Calling any mise command that publishes lockfile sidecars (install/lock resolution with lockfile_enabled) while another process switches the source lockfile symlink between read and publish; concurrent `mise lock --upgrade` or monorepo migration in another terminal; an editor/CI job swapping the symlink target during a long install.","commonSituations":"Two concurrent mise commands (e.g. `mise install` and `mise lock --upgrade`) racing on the same project; a deployment script rotating the lockfile symlink while a build agent installs tools; manual `ln -sfn` edits during a running install.","solutions":["Rerun the exact same mise command; the check is intentionally retry-safe.","Ensure only one mise process mutates the lockfile at a time (serialize installs and `mise lock` invocations in scripts/CI).","If a migration or upgrade switched the symlink deliberately, run the new command against the current deployment instead of the interrupted one."],"exampleFix":"// before: concurrent commands race on the symlink\nmise install &  mise lock --upgrade & wait\n// after: serialize lockfile-mutating commands\nmise lock --upgrade && mise install","handlingStrategy":"retry","validationCode":"# before running mise, confirm the lockfile symlink is stable\nreadlink mise.lock && readlink mise.lock  # same target twice => stable","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Never run concurrent mise commands that mutate lockfiles in the same project.","Serialize `mise install` and `mise lock --upgrade` in scripts with && or a lock.","Avoid manual `ln -sfn` on mise.lock while builds are running."],"tags":["lockfile","concurrency","symlink","retry"],"backgroundTag":"invalid-state-transition","analyzedSha":"533346cc374382b41ec5ff70536252b2e96e725c","analyzedAt":"2026-09-17T13:35:38.149Z","contentChangedAt":"2026-09-17T13:35:38.149Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}