What does a Gradle lockfile look like, and what happens when resolution no longer matches it?
answer
- group:module:version=configs
- empty= line for no-dep configs
- single gradle.lockfile (v6+)
- mismatch → build fails fail-closed
- don't hand-edit, regenerate
basics
~10 sgradle.lockfile lists group:artifact:version=configurations lines plus an empty= line. On a normal build, if resolution would differ from these pins, Gradle fails with a lock-state mismatch error telling you what changed.
solid answer
~40 sSince Gradle 6 there's a single `gradle.lockfile` per project. Each non-comment line is `group:module:version=comma,separated,configuration,names`, sorted, and a trailing `empty=` line names configurations that legitimately resolve to nothing. The file is human-readable and committed to VCS, so a PR diff shows exactly which versions moved. When a locked configuration resolves during a normal (non-write) build, Gradle compares the result to the lockfile: if a locked version is now unavailable, a newly-required module isn't in the lockfile, or a locked module is no longer required, the build **fails** with a clear message listing the discrepancies. The fix is intentional: either correct your constraints so resolution matches again, or regenerate with `--write-locks`/`--update-locks`. This fail-closed behavior is what guarantees reproducibility — silent drift becomes an explicit, reviewable error.
code
toml · 4 lines# gradle.lockfile (excerpt) — NOT actually TOML, line-oriented text
org.apache.commons:commons-lang3:3.12.0=compileClasspath,runtimeClasspath
com.google.guava:guava:32.1.2-jre=runtimeClasspath
empty=annotationProcessorgo deeper
Recognize gradle.lockfile lists pinned coordinates and that mismatches fail the build.
Explain the line format, the empty= line, and that the failure names the offending configuration/coordinate.
Detail all mismatch kinds (version/missing/extra), why fail-closed matters for reproducibility, and how to resolve via the flags.
Use lockfile diffs as a review/audit artifact and combine with STRICT mode + dependency verification for supply-chain integrity.
## The single-file format (Gradle 6+) One `gradle.lockfile` sits at each project root: ``` # This is a Gradle generated file for dependency locking. # Manual edits can break the build and are not advised. # This file is expected to be part of source control. org.apache.commons:commons-lang3:3.12.0=compileClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath com.google.guava:guava:32.1.2-jre=runtimeClasspath,testRuntimeClasspath empty=annotationProcessor,testAnnotationProcessor ``` - Lines are `group:module:version=<configurations>`; the right side lists every configuration that resolved that coordinate. - The `empty=` line records configurations that are locked but resolved to no dependencies — important so an unexpectedly *non-empty* resolution is also caught. - The legacy format (Gradle 4.8–5.x) used one file per configuration under `gradle/dependency-locks/`; you can migrate to the single file. ## What a mismatch is During a normal build, after a locked configuration resolves, Gradle checks the outcome against the lockfile. A failure is raised when any of these happen: - **Different version**: resolution would pick a version other than the locked one (e.g. a dynamic version moved, or a constraint changed). - **Missing entry**: a dependency is now required that has no line in the lockfile. - **Out-of-date entry**: a locked module is no longer in the resolved graph. ## The error Gradle reports something like: ``` Execution failed for task ':compileJava'. > Could not resolve all dependencies for configuration ':compileClasspath'. > Resolved 'com.google.guava:guava:33.0.0-jre' which is not part of the dependency lock state ``` It names the configuration and the specific coordinates that don't match. ## Resolving a mismatch The failure is by design — you must make an explicit choice: 1. **Intended change** (you upgraded a dep): regenerate with `--write-locks` (broad) or `--update-locks g:m` (targeted), then commit the new lockfile. 2. **Unintended drift** (a dynamic version moved underneath you): pin or constrain the version so resolution matches the existing lock again, or deliberately accept and re-lock. ## Don't hand-edit The header warns against manual edits; lines are sorted and the `empty=` accounting must stay consistent. Always regenerate through the flags. Combined with `LockMode.STRICT`, this fail-closed contract is what turns 'my build broke on CI but works locally' into a deterministic, diff-able event.
- Why does the lockfile bother recording an `empty=` line?So a configuration that is supposed to resolve to nothing but suddenly pulls in a dependency is also flagged as a mismatch — it locks the absence, not just the presence, of dependencies.
- A teammate hand-edited gradle.lockfile to bump a version. Why is that risky?The header warns against it: lines are sorted and the empty accounting must stay consistent, and a hand-picked version may not satisfy the real constraints, leading to confusing failures. Regenerate via --update-locks/--write-locks instead.
saying these in an interview costs you the question
- Saying a mismatch is auto-resolved by picking the newer version — it fails the build (fail-closed).
- Recommending manual edits to gradle.lockfile.
- Thinking only version changes are caught — missing and extra dependencies are too.