Skip to content

fix(install): prevent --locked mode from modifying or bypassing lockfile#8362

Merged
jdx merged 1 commit intomainfrom
fix/locked-mode-lockfile
Feb 27, 2026
Merged

fix(install): prevent --locked mode from modifying or bypassing lockfile#8362
jdx merged 1 commit intomainfrom
fix/locked-mode-lockfile

Conversation

@jdx
Copy link
Owner

@jdx jdx commented Feb 26, 2026

Summary

  • mise lock now refuses to run in --locked mode with a clear error and hint
  • Tool resolution in --locked mode fails with an error when a config-sourced tool is missing from the lockfile, instead of silently falling through to remote resolution
  • mise use tool@latest no longer bypasses the lockfile in --locked mode

Closes #8305

Test plan

  • mise run lint-fix passes
  • All 491 unit tests pass
  • MISE_LOCKED=1 mise lock should error with hint message
  • mise install --locked with a valid lockfile should not modify it
  • mise install --locked for a tool not in lockfile should error

🤖 Generated with Claude Code


Note

Medium Risk
Behavior changes in --locked mode can break existing workflows that relied on implicit remote resolution or running mise lock while locked. Impact is limited to locked/lockfile paths and adds coverage via e2e tests.

Overview
Enforces strict --locked behavior around lockfiles. mise lock now exits with an error (and hint) whenever --locked/MISE_LOCKED=1 is active, preventing lockfile updates in locked mode.

Tool version resolution now fails fast in locked mode when a config-sourced tool/version is missing from the lockfile instead of falling back to remote resolution, and mise use tool@latest no longer disables lockfile usage when locked.

Adds an e2e test (test_lockfile_locked_mode) covering that mise lock is refused under locked mode.

Written by Cursor Bugbot for commit 57f5be6. This will update automatically on new commits. Configure here.

@gemini-code-assist
Copy link
Contributor

Summary of Changes

Hello @jdx, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request enhances the --locked mode functionality by enforcing stricter adherence to lockfiles across several mise commands. It prevents mise lock from running in a locked context, ensures that tool resolution fails if a required tool is absent from the lockfile, and stops mise use tool@latest from overriding locked versions. These changes collectively improve the reliability and predictability of environments managed with lockfiles.

Highlights

  • Prevent mise lock in --locked mode: The mise lock command now explicitly errors and provides a hint when run in --locked mode, preventing unintended modifications or bypasses of the lockfile.
  • Stricter tool resolution in --locked mode: When in --locked mode, if a config-sourced tool is not found in the lockfile, mise will now error instead of silently attempting remote resolution, ensuring lockfile integrity.
  • Respect lockfile for mise use tool@latest: The mise use tool@latest command no longer bypasses the lockfile when --locked mode is active, ensuring that only versions specified in the lockfile are used.
Changelog
  • src/cli/lock.rs
    • Added bail import from eyre.
    • Implemented a check to prevent mise lock from executing when settings.locked is true, returning an error with a helpful hint.
  • src/cli/use.rs
    • Modified the logic for resolving @latest versions to only apply when Settings::get().locked is false, ensuring lockfile respect.
  • src/toolset/tool_version.rs
    • Added bail import from eyre.
    • Introduced a check within ToolVersion::resolve to error if settings.locked is true and a requested tool is not found in the lockfile, providing a hint to update the lockfile.
Activity
  • The author has outlined a test plan, including passing mise run lint-fix and all 491 unit tests.
  • Specific test cases for --locked mode behavior with mise lock, mise install, and missing tools in the lockfile have been defined.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request correctly implements the described fixes to prevent --locked mode from modifying or bypassing the lockfile. The changes in lock.rs, use.rs, and tool_version.rs are logical and address the specified scenarios. The existing suggestion to improve the consistency of error handling in locked mode is valid and has been retained, as it does not conflict with any established rules. Overall, the changes are solid.

Comment on lines +57 to +63
if Settings::get().locked && request.source().path().is_some() {
bail!(
"{}@{} is not in the lockfile\nhint: Run `mise install` without --locked to update the lockfile",
request.ba().short,
request.version()
);
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

While the current logic is correct for config-sourced tools, it could be improved for consistency. Argument-sourced tools that are missing from the lockfile currently fall through to remote resolution and fail later inside backend.install_version with a less specific error message (No lockfile URL found...).

To provide a more consistent and earlier failure for all tool requests that should be in the lockfile, you could change this condition to check the ToolRequest type instead of the source. This would catch missing tools from any source (arguments, environment, etc.) at this point with a clear error message, and correctly ignore path: and system versions which are not expected in the lockfile.

This would unify the error handling for locked mode and prevent an unnecessary remote resolution attempt for argument-sourced tools.

Suggested change
if Settings::get().locked && request.source().path().is_some() {
bail!(
"{}@{} is not in the lockfile\nhint: Run `mise install` without --locked to update the lockfile",
request.ba().short,
request.version()
);
}
if Settings::get().locked && !matches!(&request, ToolRequest::Path { .. } | ToolRequest::System { .. }) {
bail!(
"{}@{} is not in the lockfile\nhint: Run `mise install` without --locked to update the lockfile",
request.ba().short,
request.version()
);
}

@greptile-apps
Copy link

greptile-apps bot commented Feb 26, 2026

Greptile Summary

This PR adds three critical enforcement checks to prevent the lockfile from being modified or bypassed when --locked mode is enabled:

  • src/cli/lock.rs: Added early validation to prevent mise lock from running in --locked mode with a clear error message directing users to remove the flag or unset the environment variable
  • src/cli/use.rs: Modified @latest version resolution to respect --locked mode by preventing use_locked_version from being set to false, ensuring lockfile versions are used instead of fetching remote latest versions
  • src/toolset/tool_version.rs: Added enforcement to fail with a descriptive error when config-sourced tools are missing from the lockfile during --locked mode, preventing silent fallback to remote resolution

The changes correctly implement the lockfile integrity guarantees for --locked mode. One minor edge case was identified regarding tools with user-linked versions (symlinks to system installations) - these tools might not need to be in the lockfile since they're managed externally, but the current check doesn't exclude them.

Confidence Score: 4/5

  • This PR is safe to merge with one minor edge case to consider
  • The implementation correctly enforces lockfile integrity in --locked mode with clear error messages. The changes are well-targeted and address the stated objectives. Score of 4 reflects the potential edge case with linked versions (tools symlinked to system installations) that might not need lockfile entries but would currently error in --locked mode - though this is likely a rare scenario in practice.
  • src/toolset/tool_version.rs should be reviewed for the linked version edge case

Important Files Changed

Filename Overview
src/cli/lock.rs Added early check to prevent mise lock from running in --locked mode with clear error message and hint
src/cli/use.rs Modified @latest resolution to respect --locked mode by preventing use_locked_version from being disabled
src/toolset/tool_version.rs Added enforcement to fail when config-sourced tools missing from lockfile in --locked mode; potential edge case with linked versions needs consideration

Fix All in Claude Code

Last reviewed commit: 9030466

Copy link

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

3 files reviewed, 1 comment

Edit Code Review Agent Settings | Greptile

Comment on lines +57 to +63
if Settings::get().locked && request.source().path().is_some() {
bail!(
"{}@{} is not in the lockfile\nhint: Run `mise install` without --locked to update the lockfile",
request.ba().short,
request.version()
);
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this check also exclude tools with linked versions (like the lockfile resolution check above)?

Tools with linked versions (user-created symlinks to system installations) are skipped in the lockfile resolution at line 52 with !has_linked_version(request.ba()). Since these tools are managed outside mise's lockfile system, they probably shouldn't be required to be in the lockfile during --locked mode.

Consider adding the same check here:

Suggested change
if Settings::get().locked && request.source().path().is_some() {
bail!(
"{}@{} is not in the lockfile\nhint: Run `mise install` without --locked to update the lockfile",
request.ba().short,
request.version()
);
}
if Settings::get().locked && !has_linked_version(request.ba()) && request.source().path().is_some() {
bail!(
"{}@{} is not in the lockfile\nhint: Run `mise install` without --locked to update the lockfile",
request.ba().short,
request.version()
);
}

Fix in Claude Code

@github-actions
Copy link

github-actions bot commented Feb 26, 2026

Hyperfine Performance

mise x -- echo

Command Mean [ms] Min [ms] Max [ms] Relative
mise-2026.2.21 x -- echo 27.2 ± 0.3 26.7 31.7 1.00
mise x -- echo 27.5 ± 0.8 26.8 40.3 1.01 ± 0.03

mise env

Command Mean [ms] Min [ms] Max [ms] Relative
mise-2026.2.21 env 26.6 ± 0.9 26.0 41.4 1.00
mise env 26.7 ± 0.2 26.2 27.8 1.01 ± 0.04

mise hook-env

Command Mean [ms] Min [ms] Max [ms] Relative
mise-2026.2.21 hook-env 27.4 ± 0.4 26.8 31.0 1.00
mise hook-env 27.5 ± 0.2 27.0 28.6 1.00 ± 0.02

mise ls

Command Mean [ms] Min [ms] Max [ms] Relative
mise-2026.2.21 ls 25.6 ± 0.2 25.1 26.7 1.00
mise ls 25.7 ± 0.2 25.2 26.8 1.01 ± 0.01

xtasks/test/perf

Command mise-2026.2.21 mise Variance
install (cached) 168ms 168ms +0%
ls (cached) 94ms 94ms +0%
bin-paths (cached) 98ms 97ms +1%
task-ls (cached) 824ms ⚠️ 2750ms -70%

⚠️ Warning: task-ls cached performance variance is -70%

@jdx jdx force-pushed the fix/locked-mode-lockfile branch from 9030466 to 0324004 Compare February 26, 2026 12:58
@jdx jdx force-pushed the fix/locked-mode-lockfile branch from 0324004 to 964ec72 Compare February 26, 2026 13:52
In --locked mode, `mise lock` now refuses to run, tool resolution fails
with a clear error when a config-sourced tool is missing from the lockfile,
and `mise use @latest` no longer bypasses the lockfile.

Closes #8305

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@jdx jdx force-pushed the fix/locked-mode-lockfile branch from 964ec72 to 57f5be6 Compare February 26, 2026 13:57
Copy link

@cursor cursor bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Bugbot Autofix is OFF. To automatically fix reported issues with Cloud Agents, enable autofix in the Cursor dashboard.

EOF

assert_fail_contains "mise lock --locked 2>&1" "mise lock is disabled in --locked mode"
MISE_LOCKED=1 assert_fail_contains "mise lock 2>&1" "mise lock is disabled in --locked mode"
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

E2E test doesn't propagate MISE_LOCKED to subprocess

Medium Severity

MISE_LOCKED=1 assert_fail_contains "..." sets MISE_LOCKED as a shell variable via prefix assignment to a function call, but does NOT export it. Inside quiet_assert_fail, the command runs via bash -c "$1 2>&1" — a new process that only inherits exported environment variables. Since MISE_LOCKED is never exported, mise lock won't see it, won't enter locked mode, and won't produce the expected error message. The test will fail for the wrong reason or unexpectedly pass/fail.

Fix in Cursor Fix in Web

@jdx jdx merged commit d03e421 into main Feb 27, 2026
48 of 51 checks passed
@jdx jdx deleted the fix/locked-mode-lockfile branch February 27, 2026 13:00
risu729 pushed a commit to risu729/mise that referenced this pull request Feb 27, 2026
…ile (jdx#8362)

## Summary
- `mise lock` now refuses to run in `--locked` mode with a clear error
and hint
- Tool resolution in `--locked` mode fails with an error when a
config-sourced tool is missing from the lockfile, instead of silently
falling through to remote resolution
- `mise use tool@latest` no longer bypasses the lockfile in `--locked`
mode

Closes jdx#8305

## Test plan
- [x] `mise run lint-fix` passes
- [x] All 491 unit tests pass
- [ ] `MISE_LOCKED=1 mise lock` should error with hint message
- [ ] `mise install --locked` with a valid lockfile should not modify it
- [ ] `mise install --locked` for a tool not in lockfile should error

🤖 Generated with [Claude Code](https://claude.com/claude-code)

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Medium Risk**
> Behavior changes in `--locked` mode can break existing workflows that
relied on implicit remote resolution or running `mise lock` while
locked. Impact is limited to locked/lockfile paths and adds coverage via
e2e tests.
> 
> **Overview**
> **Enforces strict `--locked` behavior around lockfiles.** `mise lock`
now exits with an error (and hint) whenever `--locked`/`MISE_LOCKED=1`
is active, preventing lockfile updates in locked mode.
> 
> Tool version resolution now fails fast in locked mode when a
config-sourced tool/version is missing from the lockfile instead of
falling back to remote resolution, and `mise use tool@latest` no longer
disables lockfile usage when locked.
> 
> Adds an e2e test (`test_lockfile_locked_mode`) covering that `mise
lock` is refused under locked mode.
> 
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
57f5be6. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
mise-en-dev added a commit that referenced this pull request Feb 28, 2026
### 🚀 Features

- **(backend-plugin)** pass options to vfox backend plugins by
@Attempt3035 in [#8369](#8369)

### 🐛 Bug Fixes

- **(install)** prevent --locked mode from modifying or bypassing
lockfile by @jdx in [#8362](#8362)
- **(install)** clear aqua bin_paths cache after install to prevent
stale PATH by @jdx in [#8374](#8374)
- **(task)** prevent broken pipe panic and race condition in remote git
task cache by @vmaleze in [#8375](#8375)

### 📦️ Dependency Updates

- update docker/build-push-action digest to 10e90e3 by @renovate[bot] in
[#8367](#8367)
- update fedora:43 docker digest to 781b764 by @renovate[bot] in
[#8368](#8368)

### 📦 Registry

- add porter
([github:getporter/porter](https://github.com/getporter/porter)) by
@lbergnehr in [#8380](#8380)
- add entire ([aqua:entireio/cli](https://github.com/entireio/cli)) by
@TyceHerrman in [#8378](#8378)
- add topgrade
([aqua:topgrade-rs/topgrade](https://github.com/topgrade-rs/topgrade))
by @TyceHerrman in [#8377](#8377)

### Chore

- remove pre-commit config and tool dependency by @jdx in
[#8373](#8373)

### New Contributors

- @Attempt3035 made their first contribution in
[#8369](#8369)
- @lbergnehr made their first contribution in
[#8380](#8380)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant