# Docker failed to calculate checksum of ref, in CI

> Docker failed to calculate checksum of ref is the real wording. The compute variant and the file changed during build line are emitted by nothing.

Source: https://latchkey.dev/learn/docker/docker-checksum-file-changed-during-build-in-ci  
Updated: 2026-09-21

Docker failed to calculate checksum of ref is BuildKit naming the content reference it was hashing when a path under it could not be read. Two nearby sentences that circulate widely, the compute variant of this one and a claim about a file changing during the build, are not produced by any Docker component, and searching for them is why this failure is so hard to research.

## What this error means

A build fails on a COPY or ADD with a long opaque identifier in the middle of the message and a quoted path at the end. The identifier changes every run, which makes the failure look non deterministic even when it is completely deterministic. People then search for a wording they half remember and find nothing, which is the actual reason this one wastes an afternoon. The block below is one build on this machine.

```Captured locally on Docker 29.6.2 and buildx v0.35.0-desktop.2, 2026-09-21
ERROR: failed to build: failed to solve: failed to compute cache key: failed to calculate checksum of ref 5f708b38-db62-4035-8237-7ab78553c831::x6bt8n2ui74ux1ehnde4vorg0: "/nope.txt": not found
```

## Common causes

### The path is not in the tree being hashed

Overwhelmingly the reason, and the same reason as the COPY page linked below, because they are the same failure seen at two levels of wrapping. The path is absent from the context, excluded by an ignore file, or resolved against a root you did not expect. The quoted path with its leading slash removed is what to go and list.

### The tree being hashed is a stage, not the context

A `COPY --from` or a bind mount from another stage hashes that stage's filesystem. When the quoted path looks like something that only exists inside a builder image, you are looking at a stage and no amount of checking your repository will help. Check which stage the failing line reads from first.

### Something really did remove the file mid build

Uncommon, and it announces itself in no message of its own, but not impossible: a job that runs a cleanup in the background, a test suite writing into the context directory, or a cache restore racing the build. The signature is a failure that does not reproduce on a rerun of the identical commit, and the fix is sequencing rather than anything in the Dockerfile.

### You are reading a message from a different tool entirely

Both real sentences in our search table come from elsewhere: file changed as we read it is GNU tar, and the write too long one is the Go standard library archive package, which several deployment tools use to pack a context of their own. If your log carries either, the program to look at is the one that tars, not Docker.

## How to fix it

### Search for the wording that exists

1. Use the phrase with the word calculate in it, and drop the ref identifier from the query.
2. Prefer the longest run of letters without numerals, because a quoted phrase containing a number is not searched as a phrase on GitHub.
3. When a search returns a large count, open the results and check the text is really in them before you believe any of it.

```Terminal
# the fragment worth searching
failed to calculate checksum of ref
```

### Turn the quoted path into a thing you can list

Strip the leading slash and you have the path relative to the root of whatever tree was hashed. For a context that is the build context directory. Listing it in the job immediately before the build settles in one run whether this is a missing file or a wrong root.

```.github/workflows/ci.yml
- run: ls -la "$(echo '/nope.txt' | sed 's#^/##')" || echo "not in the context"
- run: docker build -t acme/api:ci .
```

### Stop anything from writing into the context while the build runs

A context that is still being written is a bad idea for reasons beyond this error, including a cache key that varies for no visible reason. Finish generation before the build starts, and exclude output directories that do not belong in the image.

```.dockerignore
# .dockerignore
dist
coverage
.cache
**/*.log
```

### Rerun once before you investigate, and record which branch you took

Because a genuine mid build mutation does not reproduce and everything else does, one rerun of the identical commit splits the two cases cleanly. Do it deliberately and note the result, rather than rerunning by reflex until something passes, which is how a sequencing bug becomes permanent.

```.github/workflows/ci.yml
- name: Build
  id: b1
  continue-on-error: true
  run: docker build -t acme/api:ci .

- name: One deliberate rerun
  if: steps.b1.outcome == 'failure'
  run: |
    echo "first attempt failed; if this passes the context was not stable"
    docker build -t acme/api:ci .
```

## How to prevent it

- Sequence every step that writes into the build context before the build, never alongside it.
- Exclude generated directories so the builder never hashes a tree that is still moving.
- Search build errors on their longest alphabetic fragment, not on the whole line.
- When you quote an error in your own documentation, copy it from a terminal rather than from memory.

## Two sentences people quote that nothing emits

We went looking for both, with controls, because a page in our own older corpus quoted one of them. BuildKit wraps this failure with the word calculate. There is no compute variant anywhere in its source, and there is no sentence about a file changing during a build in BuildKit, in its file transfer library, or in the moby daemon.

The searching is worth describing, because counts on their own are worthless here. Code search across all of GitHub for the compute variant returned exactly one result, and that result was a page in our own Learn corpus. Issue search reported 1728 results for it, and of ten issue bodies and comment threads we fetched, zero contained the string. The same ten sources, searched for the real wording, contained it ten times out of ten. A nonsense control phrase returned nothing at all.

| Phrase searched | Issue bodies containing it, of ten fetched |
| --- | --- |
| `failed to calculate checksum of ref` | 10 |
| `failed to compute checksum of ref` | 0 |
| `file changed as we read it`, the GNU tar message | 10 |
| `archive/tar: write too long`, the Go archive message | 9 |
| A nonsense alphabetic control phrase | 0, and nothing was returned to fetch |

> Run on 2026-09-21. Each search fetched the issue body and up to thirty comments and tested for the literal string, because reported result counts on GitHub are not a measure of anything when a phrase carries a numeral.

## What the parts of the real message mean

A ref here is a BuildKit content reference, an internal handle for a tree of files, and the identifier is generated per build. It is not a cache key you can look up, not a layer digest, and not anything you can act on. Its only function in the message is to tell BuildKit engineers which reference the hash was over.

The quoted path at the end is the part that concerns you, and it is always absolute because it is resolved against the root of whatever tree was being hashed. For a build context that root is the context directory. The wrapper above this one, about a cache key, and the six programs that assemble the whole line are covered on the COPY page linked below; this page is about the checksum sentence itself and the folklore around it.

## The race story has no message behind it

The story attached to the missing sentence is that a file rewritten while the builder reads it fails the build and says so. No component says so. The wording is not in BuildKit, not in its file transfer library and not in the moby daemon, which is the search the top of this page rests on, and a failure mode with no wording of its own cannot be the thing your log is telling you. Anyone who quotes that sentence back at you is quoting something they assembled from memory.

A context that really is moving under the builder produces this same checksum sentence, naming whichever path went missing at the moment it was hashed, or a cache key that changes between runs for no visible reason. Both are worth avoiding on their own merits. But since the signature people describe does not exist, a build that failed once and passed on a rerun with no change is better explained by a step ordering problem, a cache restore racing the build, or a builder that went away, each of which does have wording of its own.

## Why no recorded run backs this page

This page is mostly a negative result, and a negative result is the one thing a recorded run cannot deliver. A Latchkey job that passed would prove nothing about whether a sentence exists, and a job that failed would only show us the message we already captured locally.

The evidence that suits the claim is the evidence we gathered: the source of the wrapper that adds the word calculate, a code search across GitHub whose only hit for the wrong variant was our own corpus, and an issue search with the bodies actually fetched and grepped. Spending runner minutes here would have bought a screenshot and no argument.

## FAQ

### Is it failed to compute checksum or failed to calculate checksum?

Calculate. That is the word in the wrapper that adds this sentence, and a code search across GitHub for the compute variant returned a single hit which was a page in our own older corpus. Of ten issue threads we fetched, ten contained the real wording and none contained the other one.

### Does Docker ever say a file changed during the build?

Not in any component we could find. The sentence is not in BuildKit, in its file transfer library or in the daemon. Two real messages about files changing do exist, one from GNU tar and one from the Go standard library archive package, and neither is emitted by a Docker build.

### What is the ref identifier in the middle of the message?

An internal BuildKit content reference, generated per build. It identifies the tree the hash was being taken over and means nothing outside BuildKit. Including it in a search guarantees no results, so remove it before you look anything up.

### Can a file being written during the build really break it?

In principle yes, and nothing in your log will say so, because the sentence people quote for it exists in no Docker component we could find. What a moving context actually produces is this same checksum message naming the path that went missing. Fix a context that is still being written, but keep it well down your list of suspects for a build that failed once.

## References

- [BuildKit: the wrapper that names the ref and the word calculate](https://github.com/moby/buildkit/blob/master/solver/llbsolver/ops/opsutils/contenthash.go)
- [BuildKit: the checksum code that returns not found for a path](https://github.com/moby/buildkit/blob/master/cache/contenthash/checksum.go)
- [openremote/openremote#3025: the real wording in a reported build failure](https://github.com/openremote/openremote/issues/3025)
- [Go: the archive/tar write too long error, which is not a Docker message](https://pkg.go.dev/archive/tar#pkg-variables)

---

Latchkey runs CI/CD that repairs its own failures. Agent entry points: https://latchkey.dev/agent.txt, https://latchkey.dev/openapi.json, https://latchkey.dev/llms.txt
