failed to resolve source metadata for docker.io/library/fix9-bld-buildd:latest: pull access denied, repository does not exist or may require authorization in a multi-stage build, with a name you meant as a stage, means the name in COPY --from= does not match any AS name in the Dockerfile. The build then treats it as an image and asks Docker Hub for it. Correct the spelling.

The message ends with a hint: (did you mean fix9-bld-build?). Use the stage name it suggests, exactly as written after AS.

The error

#3 [internal] load metadata for docker.io/library/fix9-bld-buildd:latest
#3 ERROR: pull access denied, repository does not exist or may require authorization: server message: insufficient_scope: authorization failed
------
 > [internal] load metadata for docker.io/library/fix9-bld-buildd:latest:
------
Dockerfile:6
--------------------
   4 |
   5 |     FROM alpine:latest
   6 | >>> COPY --from=fix9-bld-buildd /src/app.txt /app/app.txt
   7 |     CMD ["cat", "/app/app.txt"]
   8 |
--------------------
ERROR: failed to build: failed to solve: fix9-bld-buildd: failed to resolve source metadata for docker.io/library/fix9-bld-buildd:latest: pull access denied, repository does not exist or may require authorization: server message: insufficient_scope: authorization failed (did you mean fix9-bld-build?)

Why it happens

COPY --from= accepts either the name of an earlier stage or an image reference. The first stage here was declared AS fix9-bld-build and the copy asked for fix9-bld-buildd, one extra letter. No stage had that name, so the build expanded it to docker.io/library/fix9-bld-buildd:latest and tried to load its metadata from Docker Hub, as step #3 shows. No repository of that name exists, and the answer came back as "pull access denied ... may require authorization". The wording talks about authorization, but the cause here was only the spelling.

The failure came before any stage ran: the output has no RUN step at all. The misspelled name cost one request to Docker Hub. If the typo happens to be the name of a real public image, the build would copy from that image instead; that case was not tested here.

The fix

FROM alpine:latest AS fix9-bld-build
WORKDIR /src
RUN echo "built" > /src/app.txt

FROM alpine:latest
COPY --from=fix9-bld-build /src/app.txt /app/app.txt
CMD ["cat", "/app/app.txt"]

With the name matching the AS line, docker build --progress=plain -t fix9-bld-c3 . ended with exit code: 0, and docker run --rm fix9-bld-c3 printed built. Short stage names such as build and publish are easy to mistype; copying the name from the AS line instead of retyping it avoids the extra letter.

How it was reproduced

A two-stage Dockerfile on alpine:latest (image id 294b683cb724) with one letter added to the stage name in COPY --from, built with docker build --progress=plain -t fix9-bld-c3 . on Docker Desktop for Windows, client and engine 29.9.3, buildx v0.38.1. The stage, image and container names carry the test's own prefix so the misspelled name could not exist on Docker Hub.

Frequently asked

Why does COPY --from try to pull an image in docker build?
The name after --from matched no stage in the Dockerfile, so it was treated as an image name and looked up on Docker Hub. Check the spelling against the AS name.
What does 'did you mean' at the end of a docker build error mean?
In this test it named the stage whose spelling was closest to the one in COPY --from. Using that name fixed the build.
Is pull access denied in a multi-stage build a login problem?
Not in this case. The name was a misspelled stage, so no repository of that name existed on Docker Hub; correcting the spelling fixed the build.

More decoded errors in the Fixes category. The same message from docker pull or docker run, with a misspelled image name, is covered in pull access denied, repository does not exist.