Unable to download artifact: Artifact not found
Fix "Unable to download artifact: Artifact not found": match the upload and download names, order jobs with needs, and handle the v4 changes. Covers expiry.
Error: Unable to download artifact(s): Artifact not found for name: build-output
Incident Overview
Fix "Unable to download artifact: Artifact not found": match the upload and download names, order jobs with needs, and handle the v4 changes. Covers expiry.
easy fix·6 min read
By Vishvesh Patel·DevOps Engineer·Updated 21 September 2026
Error: Unable to download artifact(s): Artifact not found for name: build-output Error: Create Artifact Container failed: The artifact name build-output is not valid ERROR: Downloading artifacts from coordinator... not found id=12345 responseStatus=404 Not Found Error: Conflict: an artifact with this name already exists on the workflow run
Do this first3 steps
Run these in order. Each one tells you what its output means before you change anything.
• Compare the upload name against the download name, exactly
The name is a literal string match and is the cause most of the time. An expression such as build-${{ matrix.os }} produces a different name per matrix leg, and a download naming the bare prefix finds nothing.
• Confirm the producing job ran and actually uploaded something
upload-artifact warns rather than failing when its path matches no files, so a job can succeed having uploaded nothing. Set if-no-files-found to error to turn that into a real failure.
• Check the jobs are ordered, not parallel
Without needs in GitHub Actions or a later stage in GitLab, the consuming job may start before the producer finishes. It then downloads nothing and the failure looks intermittent.
The consuming job asked for an artifact that is not there. Four causes, in the order they occur.
grep -n -A3 "upload-artifact\|download-artifact" .github/workflows/*.yml
1. The name does not match
The name is a literal string. The most common failure is an expression producing a different one per job:
Either name them consistently, or download everything:
pattern and merge-multiple were added in v4 precisely for the matrix case.
- uses: actions/upload-artifact@v4
with:
name: build-${{ matrix.os }} # build-ubuntu-latest, build-macos-latest
path: dist/
- uses: actions/download-artifact@v4
with:
name: build # matches nothing2. v4 changed the rules
This broke a great many pipelines and is worth knowing in full:
In v3 every matrix leg uploading build merged silently. In v4 the second one fails. Give each a unique name and merge on download:
Do not mix versions: a v4 download cannot see a v3 upload, and the reverse also fails.
Error: Conflict: an artifact with this name already exists on the workflow run
3. The upload silently matched nothing
If dist/ does not exist, this warns and succeeds. The job is green and no artifact exists, and the failure surfaces later in a different job, which is a confusing place to debug it.
Worth setting on every upload. It converts a silent miss into a failure at the point where the problem actually is.
Check the path too: it is relative to the workspace, and a build that writes to packages/app/dist is not matched by dist/.
- uses: actions/upload-artifact@v4
with:
name: build
path: dist/4. Job ordering
Without needs, jobs run concurrently and the consumer may start before the producer finishes. That produces an intermittent failure that passes on a slow build and fails on a fast one.
In GitLab, stage order handles it:
GitLab passes artifacts from all earlier stages by default. dependencies narrows it, and an empty list disables the download entirely:
That is worth setting on jobs that need no artifacts, since a large artifact downloaded into every job costs real time.
publish:
needs: build # without this, they run in parallelExpiry
GitLab artifacts expire, and the default is often shorter than people assume:
A 404 on a job from last month is usually expiry rather than a fault. expire_in: never keeps them, at a storage cost. Use keep_latest_artifact in project settings to retain the most recent per ref.
GitHub Actions artifacts default to 90 days, configurable per upload:
Shorter retention is worth setting for large build outputs, since artifact storage counts against your account quota.
artifacts: paths: [dist/] expire_in: 1 week
Across workflows
In v4 this needs explicit parameters:
Without run-id and a token, the action only looks within the current run. This is the standard pattern for a workflow_run workflow consuming output from the workflow that triggered it.
- uses: actions/download-artifact@v4
with:
name: build
run-id: ${{ github.event.workflow_run.id }}
github-token: ${{ secrets.GITHUB_TOKEN }}
repository: ${{ github.repository }}Artifacts are not a cache
Worth stating because it causes both errors and cost:
Using artifacts for node_modules is slow and expensive. Use the cache action, which is designed to tolerate a miss.
A checklist
Frequently Asked Questions
Why did upload-artifact v4 break my matrix build?
Because v4 removed the implicit merging that v3 did. In v3, several jobs uploading under the same name were combined into one artifact; in v4 names must be unique within a run and the second upload fails with a conflict. Give each matrix leg a distinct name such as build-${{ matrix.os }}, then download with pattern: build-* and merge-multiple: true, which reassembles them into one directory. The versions are also incompatible in both directions, so upload and download must match.
Why does my upload succeed but the download find nothing?
Because upload-artifact only warns when its path matches no files; the step and the job both go green having uploaded nothing. The failure then surfaces in a different job, which is a misleading place to start debugging. Set if-no-files-found: error on every upload so the failure happens where the problem is. Also verify the path: it resolves relative to the workspace, so a monorepo writing to packages/app/dist is not matched by dist/.
Why is the failure intermittent?
Almost certainly missing job ordering. Without needs: in GitHub Actions, jobs run in parallel, so whether the consumer finds the artifact depends on which job happens to finish first. A slow build masks it and a fast one exposes it. In GitLab, jobs within a stage run in parallel and only later stages see earlier artifacts, so a consumer placed in the same stage as its producer has the same race.
What is the difference between artifacts and cache?
Artifacts pass build output between jobs and are meant to be retrievable afterwards; a missing artifact is an error your pipeline has to handle. Cache exists to speed up repeated work and is keyed, shared across runs, and safe to miss, since a cache miss simply means the job does more work. Using artifacts for dependency directories such as node_modules is slower and consumes storage quota, and using cache to pass build output between jobs is unreliable because a miss is silent.
Why do older artifacts return 404?
They expired. GitLab's expire_in is commonly set to days or weeks and defaults to a limited period, and GitHub Actions retains artifacts for 90 days by default. A download from a job run last month is therefore likely to be genuine expiry rather than a fault. Set expire_in: never or a longer retention-days for artifacts you need to keep, bearing in mind that storage counts against your account's quota and large build outputs add up quickly.
Reference and practice
Learn the underlying concept
Other CI/CD errors
Free, comprehensive DevOps tutorials and learning roadmaps. Master Docker, Kubernetes, CI/CD, and more.
Get notified about new tutorials and features.
• Roadmaps
• Interview Prep
• Certifications
• Company
© 2026 DevOpsLesson. All rights reserved.
Need to practice this scenario?
Simulate this error condition and practice recovery commands safely in our in-browser terminal.