Skip to content
DevOps AI ToolKit
Newsletter
All guides
AI for GitLab CI/CD By James Joyner IV · · 8 min read

GitLab CI Error Guide: 'manifest unknown' — Fix Missing Image Tag Pulls

Quick answer

Fix 'manifest unknown' in GitLab CI: the image tag or digest doesn't exist in the registry. Correct the tag, wait for the push, fix the registry path, and re-pull.

  • #gitlab
  • #ci-cd
  • #troubleshooting
  • #errors
Free toolkit

Fixing errors like this? Get 500 free DevOps AI prompts

500 copy-paste AI prompts for the stack you actually run — one PDF, free.

Overview

A GitLab CI job fails when it asks a registry for an image tag or digest that the registry cannot find. The runner prints this while preparing the job image or during a docker pull:

ERROR: Job failed: failed to pull image "registry.example.com/team/app:v1.4.2"
with specified policies [always]: manifest unknown: manifest unknown (manager.go:...)

The plain docker pull variant is identical in meaning:

Error response from daemon: manifest for registry.example.com/team/app:v1.4.2 not found: manifest unknown: manifest unknown

Unlike an auth error, the registry accepted your credentials — the reference itself simply doesn’t exist there.

Symptoms

  • The job never starts its script; it fails while pulling the image: or a services: image.
  • A docker pull / docker run step fails with manifest unknown for a tag you expected to exist.
  • The same repo works for other tags (e.g., :latest) but not the specific tag/digest requested.
  • A downstream job pulls an image a previous job was supposed to push, but the push hasn’t finished or failed.
  • Multi-arch pulls fail on one platform because that platform’s manifest wasn’t published.

Common Root Causes

  • Tag doesn’t exist — a typo, wrong version, or a tag that was never pushed (or was deleted by a cleanup policy).
  • Race between push and pull — a downstream job pulls before the upstream build finished pushing the tag.
  • Wrong registry path — the image name points at the wrong project/namespace or an external registry that doesn’t hold it.
  • Digest no longer present — pinning @sha256:... to a digest that expired via retention/garbage collection.
  • Cleanup policy pruned it — a GitLab Container Registry cleanup policy deleted the tag between build and deploy.
  • Multi-arch gap — the tag exists for amd64 but not the requested arm64 (or vice versa); the platform manifest is missing.
  • Case / normalization — registry paths are case-sensitive; App vs app yields a different, non-existent repo.

Diagnostic Workflow

Confirm whether the tag actually exists in the registry before touching the pipeline:

check-tag:
  image: quay.io/skopeo/stable:latest
  script:
    - skopeo login -u "$CI_REGISTRY_USER" -p "$CI_REGISTRY_PASSWORD" "$CI_REGISTRY"
    - skopeo list-tags docker://$CI_REGISTRY_IMAGE          # does the tag appear?
    - skopeo inspect docker://$CI_REGISTRY_IMAGE:$WANTED_TAG # errors if manifest unknown

Inspect the manifest and its platforms directly with the registry API or docker buildx:

inspect-manifest:
  image: docker:26.1.4
  services: [docker:26.1.4-dind]
  script:
    - docker login -u "$CI_REGISTRY_USER" -p "$CI_REGISTRY_PASSWORD" "$CI_REGISTRY"
    - docker buildx imagetools inspect $CI_REGISTRY_IMAGE:$WANTED_TAG   # lists platforms/digests

For push/pull races, gate the pulling job on the pusher with needs: so it cannot run early:

deploy:
  needs: ["build-and-push"]     # deploy only after the push job succeeds
  script:
    - docker pull $CI_REGISTRY_IMAGE:$CI_COMMIT_SHORT_SHA

Example Root Cause Analysis

A deploy job intermittently failed with manifest unknown for :$CI_COMMIT_SHORT_SHA. The pipeline built and pushed the image in one job and deployed in another, but both were in the same stage with no dependency:

stages: [ci]

build:
  stage: ci
  script:
    - docker build -t $CI_REGISTRY_IMAGE:$CI_COMMIT_SHORT_SHA .
    - docker push $CI_REGISTRY_IMAGE:$CI_COMMIT_SHORT_SHA

deploy:
  stage: ci                      # same stage — could start before build pushed
  script:
    - docker pull $CI_REGISTRY_IMAGE:$CI_COMMIT_SHORT_SHA

Because both jobs shared a stage and ran on parallel runners, deploy sometimes pulled the tag before build finished pushing it — the manifest genuinely didn’t exist yet, so the registry returned manifest unknown. The fix was to order the jobs with an explicit dependency:

stages: [build, deploy]

build:
  stage: build
  script:
    - docker build -t $CI_REGISTRY_IMAGE:$CI_COMMIT_SHORT_SHA .
    - docker push $CI_REGISTRY_IMAGE:$CI_COMMIT_SHORT_SHA

deploy:
  stage: deploy
  needs: ["build"]               # cannot pull until the push succeeded
  script:
    - docker pull $CI_REGISTRY_IMAGE:$CI_COMMIT_SHORT_SHA

The team also relaxed an over-aggressive cleanup policy that had been deleting recent SHA tags within minutes, which had caused a second, rarer instance of the same error at deploy time.

Prevention Best Practices

  • Use immutable, unique tags ($CI_COMMIT_SHORT_SHA) and reference the exact tag you pushed; avoid pulling :latest for deploys.
  • Order push→pull with needs:/stages so a consumer never runs before the producer finishes pushing.
  • Verify a tag exists (skopeo inspect / docker buildx imagetools inspect) before a deploy relies on it.
  • Tune Container Registry cleanup policies so they don’t prune tags a deploy still needs; keep a retention window that covers the release cycle.
  • For multi-arch, confirm the manifest list includes every platform you pull; publish all platforms in the same push.
  • Double-check the full registry path and case; $CI_REGISTRY_IMAGE is the canonical, correctly-cased project path.
  • When pinning digests, re-pin after any registry garbage collection that could remove the referenced blob.

Quick Command Reference

# List tags actually present in the registry
skopeo list-tags docker://registry.example.com/team/app

# Inspect a specific tag/digest (errors on manifest unknown)
skopeo inspect docker://registry.example.com/team/app:v1.4.2

# Show platforms in a manifest list (multi-arch gaps)
docker buildx imagetools inspect registry.example.com/team/app:v1.4.2

# Re-tag/push if the tag was missing
docker tag app:local registry.example.com/team/app:v1.4.2
docker push registry.example.com/team/app:v1.4.2

Conclusion

manifest unknown means the registry authenticated you but could not find the requested tag or digest — it was never pushed, was pruned by a cleanup policy, points at the wrong path, is missing for the requested platform, or is being pulled before its push completed. Verify the tag exists with skopeo/imagetools, order producers before consumers with needs:, use immutable SHA tags, and keep retention policies from deleting images a deploy still depends on.

Free download · 368-page PDF

Get 500 Battle-Tested DevOps AI Prompts — Free

500 battle-tested, copy-paste AI prompts engineered by a senior systems engineer — every one with fill-in placeholders and safety/back-out notes. Drop your email and it's yours.

  • 500 prompts: Linux · Kubernetes · Terraform · OpenStack · GitLab · Docker · Monitoring · Incident Response
  • Instant PDF download — yours free, forever
  • Plus one practical AI-workflow email a week (no spam)

Single opt-in · unsubscribe anytime · no spam.