Use the error mode in CI. It fails the build if anything consulted during module resolution is missing or out of date, and it never rewrites the lockfile or makes network requests during resolution. Keep the default update mode for developers, and let a reviewed pull request change the lockfile.
The four modes
Bazel records the result of module resolution in MODULE.bazel.lock. The lockfile_mode flag decides what Bazel does when the project differs from that file.
| Mode | Behavior | Good for |
|---|---|---|
| update (default) | Uses lockfile data to skip known downloads, adds missing information | Local development |
| refresh | Like update, but refreshes mutable data such as yanked versions, roughly hourly | A scheduled job that checks for newly yanked modules |
| error | Fails if information is missing or out of date, no changes, no resolution network requests | CI and release builds |
| off | Lockfile neither checked nor updated | Debugging only |
Module extensions that mark themselves reproducible may still make network requests in error mode, but they are expected to produce the same result every time.
Why CI should fail on drift
In update mode, a build can quietly change the lockfile. If CI runs that way and nobody commits the result, you get a build that differs from what a reviewer saw. In error mode the drift becomes a red build, which is a signal a human can act on.
The lockfile holds hashes of every remote registry file used in resolution, plus the inputs and outputs of each module extension. A lockfile change in a pull request is therefore a readable record of what your graph is about to consume.
A workable setup
- Commit MODULE.bazel.lock to version control. The Bazel docs list this as a best practice.
- Pin the Bazel version with a version file and a launcher, because the lockfile is specific to the Bazel version and can change even between compatible releases.
- In CI, run builds with the error mode set.
- Use the tidy command in a dedicated dependency update job that opens a pull request with the lockfile diff.
- Add a scheduled job in refresh mode to notice newly yanked versions without touching developer builds.
# .bazelrc
common:ci --lockfile_mode=error
What the lockfile does not give you
It does not list your packages in a scanner friendly format, and it only includes dependencies in the invocation that produced it. It also does not decide whether a module is safe. Treat it as proof that resolution was repeatable, then feed a proper inventory to your scanner.
Merge conflicts
The format is designed to limit conflicts, and Bazel documents a custom git merge driver. For other conflicts, restore the previous lockfile, fix MODULE.bazel, and regenerate with the deps command rather than hand editing.
Where Safeguard fits
Safeguard can inventory the dependencies your repository declares through SBOM and dependency scanning, and reachability analysis helps you decide which findings matter first. You can try a free scan on a repository to see what it finds. Check coverage for your specific build files before relying on it.
Quick check
Can the error mode ever change my lockfile?
No. It never changes the lockfile. A lockfile that passes in this mode may still be rewritten, pruned of superfluous entries, by update mode later.
Is the lockfile enough to scan my dependencies?
Not on its own. It records resolution results and hashes, not a package inventory. See the post on scanning dependencies in a Bazel repository.
Sources
Last reviewed: 19 October 2026.