Docker "invalid reference format" - Fix Bad Image Names in CI
Docker rejected the image reference before contacting any registry because it does not match the grammar for a valid name and tag.
What this error means
A docker build -t, docker tag, docker pull, or docker run fails instantly with invalid reference format, sometimes adding repository name must be lowercase. No network call is made.
docker: invalid reference format: repository name must be lowercase.
# e.g. from: docker build -t MyOrg/API:Latest . or an empty ${IMAGE} variableDiagnose it: separate auth from naming from rate limits
Registry errors look alike and have unrelated causes. Work out which of the three you have before changing credentials, because a malformed image reference produces an error that reads like an authentication failure.
# 1. is the reference even valid? (lowercase, no spaces, valid tag)
docker image inspect "$IMAGE" 2>&1 | head -2
# 2. are you authenticated to the right registry?
cat ~/.docker/config.json | grep -o '"[^"]*\.[^"]*"' | head
# 3. are you rate limited? (Docker Hub anonymous pulls)
curl -s "https://auth.docker.io/token?service=registry.docker.io&scope=repository:ratelimit-preview/test:pull" \
| grep -o '"token"' >/dev/null && echo "token ok"Common causes
Uppercase letters or illegal characters
Repository names must be lowercase and may only contain a restricted character set. MyOrg/API, spaces, or stray characters break the grammar.
An unsubstituted or empty variable
A reference like ${REGISTRY}/api:${TAG} where a variable is empty collapses to something malformed (/api: or :latest with a leading colon), which Docker cannot parse.
Malformed digest or double separators
A bad @sha256: digest, a double slash, or a trailing colon produces a reference that fails validation.
How to fix it
Lowercase the name and remove illegal characters
Normalize the repository name; lowercase any branch or org value interpolated into it.
IMAGE="ghcr.io/${OWNER,,}/api:${TAG}" # bash lowercases OWNER
docker build -t "$IMAGE" .Echo the reference before using it
Print the fully-expanded reference so an empty variable is obvious.
echo "building: ${REGISTRY}/api:${TAG}"
test -n "${TAG}" || { echo "TAG is empty"; exit 1; }Authenticate in the job, not in the image
- uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
# GHCR needs this on the job or the push is rejected as unauthorised
permissions:
contents: read
packages: writeHow to prevent it
- Lowercase any dynamic org/branch values before putting them in an image name.
- Default tag variables and assert they are non-empty before building.
- Keep image references in one place so the grammar is easy to audit.