{"record":{"id":"faa01e30481c33e9","repo":"jdx/mise","slug":"file-changed-during-lockfile-generation-retry-the-command","errorCode":null,"errorMessage":"file {} changed during lockfile generation; retry the command","messagePattern":"file (.+?) changed during lockfile generation; retry the command","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/cli/lock.rs","lineNumber":239,"sourceCode":"    if targets.len() < 2 {\n        return true;\n    }\n    let mut canonical = BTreeSet::new();\n    targets.all(|path| match fs::canonicalize(path) {\n        Ok(path) => canonical.insert(path),\n        // Missing files cannot take the fast path, so only existing targets\n        // need to be distinct before one of them can be skipped.\n        Err(error) if error.kind() == ErrorKind::NotFound => true,\n        Err(_) => false,\n    })\n}\n\nfn verify_generation_snapshots<'a>(\n    snapshots: impl Iterator<Item = (&'a PathBuf, &'a Option<Vec<u8>>)>,\n) -> Result<()> {\n    for (path, content) in snapshots {\n        if read_optional_file(path)? != *content {\n            bail!(\n                \"file {} changed during lockfile generation; retry the command\",\n                display_path(path)\n            );\n        }\n    }\n    Ok(())\n}\n\nfn read_optional_file(path: &Path) -> Result<Option<Vec<u8>>> {\n    match fs::read(path) {\n        Ok(content) => Ok(Some(content)),\n        Err(err) if err.kind() == ErrorKind::NotFound => Ok(None),\n        Err(err) => Err(err.into()),\n    }\n}\n\nfn prepare_lockfile_rollback(\n    snapshots: &[LockfileSnapshot],","sourceCodeStart":221,"sourceCodeEnd":257,"githubUrl":"https://github.com/jdx/mise/blob/533346cc374382b41ec5ff70536252b2e96e725c/src/cli/lock.rs#L221-L257","documentation":"During `mise lock`, mise snapshots input files (configs, lockfiles) before generation and re-reads them afterwards. If any file's content differs from its snapshot, concurrent modification occurred while the lockfile was being computed, so results would be unreliable and the command aborts.","triggerScenarios":"Another mise process (or editor/tooling that rewrites config) modifies a config file or lockfile listed in the generation snapshots between the initial read and the verification in `verify_generation_snapshots`, called from `run_with_installed`.","commonSituations":"Running two `mise lock`/`mise install` commands concurrently in the same project; an IDE plugin or `direnv` rewriting `mise.toml` mid-command.","solutions":["Re-run the same command — the error explicitly says retry","Ensure no other mise processes are running concurrently (check `mise tasks`/CI parallelism)","Disable auto-rewriting tools (editors, direnv hooks) that touch mise.toml while locking"],"exampleFix":"// before (concurrent runs in CI)\n- run: mise lock &\n- run: mise install &\n// after\n- run: mise lock\n- run: mise install","handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"// bash: retry on transient race\nfor i in 1 2 3; do mise lock && break || sleep 2; done","preventionTips":["Do not run concurrent mise lock/install commands in one project","Disable editors/plugins that auto-rewrite mise.toml during CI","Serialize mise operations in scripts"],"tags":["lockfile","concurrency","race-condition","retry"],"backgroundTag":"file-changed-during-operation","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"}