Symptom
An integration test that builds an image fails with:
build failed: alpine:3.20: failed to resolve source metadata for docker.io/library/alpine:3.20: no active sessions
Observed on test-integration-linux (1.26, 2) in PR #129 on TestEngineBuild_NoContainerCreated. The same test passed on Go 1.25 in the same shard of the same run, and passed on both versions on the two following heads — so the input that varied was the registry, not the code.
The message is misleading. It names BuildKit's session requirement, which is working exactly as designed and is not what went wrong.
Cause
BuildImage pre-pulls base images specifically so BuildKit never has to resolve remote metadata (runtime/docker/build.go, the comment above the prePullBaseImages call spells this out: BuildKit refuses remote resolution without an active session, so the classic ImagePull API seeds the local store and BuildKit picks up the cached manifest).
The pre-pull is best-effort and the error is discarded:
for _, ref := range extractBaseImages(string(df), buildArgs) {
if _, err := r.api.ImageInspect(ctx, ref); err == nil {
continue
}
_, _ = r.PullImage(ctx, ref, events)
}
So any transient pull failure — docker.io rate limiting, a DNS blip, a registry 5xx — leaves the local store unseeded, and the build then fails several layers away with BuildKit's session error. The real cause (the pull, and why it failed) is never reported anywhere.
This is what makes the flake expensive: the error points a reader at BuildKit and at session handling, and the first hypothesis on a dependency-bump branch is that the Docker client bump broke the build path. Ruling that out took a diff of moby/moby/client 0.4.1 → 0.5.1 to confirm image_build.go, image_pull.go and session handling were all untouched.
Suggested fix
Keep the pre-pull best-effort — it is legitimately advisory, since extractBaseImages deliberately drops refs it cannot resolve and a pull failure is not always fatal — but stop throwing the error away. Either:
- retain the last pull error per ref and, when
ImageBuild subsequently fails, wrap it so the build error carries the pre-pull failure that likely caused it; or
- emit the pull failure as a
BuildEvent warning at the point it happens, so it lands in the build log ahead of the confusing error.
The first gives the better diagnostic (the two errors appear together, causally ordered) and does not add per-run noise for the benign case where the ref was already local or the pull failure did not matter.
Relationship to #50
#50 deletes prePullBaseImages and extractBaseImages outright by opening a real BuildKit session with an auth provider, which removes this failure mode along with the workaround. That is the right end state, and its §2 notes a neighbouring case — a ref the parser cannot resolve falls through to the same "no active sessions" error.
This issue is narrower and worth doing independently: it is about the case where the parser resolved the ref correctly and the pull failed, and the fix is a few lines rather than a new direct dependency on github.com/moby/buildkit. If #50 lands first this becomes moot; until then, CI flakes here misdiagnose themselves.
Severity
Low, but it costs real time on every occurrence: a red CI job whose message points away from the actual cause.
Symptom
An integration test that builds an image fails with:
Observed on
test-integration-linux (1.26, 2)in PR #129 onTestEngineBuild_NoContainerCreated. The same test passed on Go 1.25 in the same shard of the same run, and passed on both versions on the two following heads — so the input that varied was the registry, not the code.The message is misleading. It names BuildKit's session requirement, which is working exactly as designed and is not what went wrong.
Cause
BuildImagepre-pulls base images specifically so BuildKit never has to resolve remote metadata (runtime/docker/build.go, the comment above theprePullBaseImagescall spells this out: BuildKit refuses remote resolution without an active session, so the classicImagePullAPI seeds the local store and BuildKit picks up the cached manifest).The pre-pull is best-effort and the error is discarded:
So any transient pull failure — docker.io rate limiting, a DNS blip, a registry 5xx — leaves the local store unseeded, and the build then fails several layers away with BuildKit's session error. The real cause (the pull, and why it failed) is never reported anywhere.
This is what makes the flake expensive: the error points a reader at BuildKit and at session handling, and the first hypothesis on a dependency-bump branch is that the Docker client bump broke the build path. Ruling that out took a diff of
moby/moby/client0.4.1 → 0.5.1 to confirmimage_build.go,image_pull.goand session handling were all untouched.Suggested fix
Keep the pre-pull best-effort — it is legitimately advisory, since
extractBaseImagesdeliberately drops refs it cannot resolve and a pull failure is not always fatal — but stop throwing the error away. Either:ImageBuildsubsequently fails, wrap it so the build error carries the pre-pull failure that likely caused it; orBuildEventwarning at the point it happens, so it lands in the build log ahead of the confusing error.The first gives the better diagnostic (the two errors appear together, causally ordered) and does not add per-run noise for the benign case where the ref was already local or the pull failure did not matter.
Relationship to #50
#50 deletes
prePullBaseImagesandextractBaseImagesoutright by opening a real BuildKit session with an auth provider, which removes this failure mode along with the workaround. That is the right end state, and its §2 notes a neighbouring case — a ref the parser cannot resolve falls through to the same "no active sessions" error.This issue is narrower and worth doing independently: it is about the case where the parser resolved the ref correctly and the pull failed, and the fix is a few lines rather than a new direct dependency on
github.com/moby/buildkit. If #50 lands first this becomes moot; until then, CI flakes here misdiagnose themselves.Severity
Low, but it costs real time on every occurrence: a red CI job whose message points away from the actual cause.