The error an artifact with this name already exists is a 409 from the artifact service, and it has been the intended behavior of GitHub Actions since version 4 of actions/upload-artifact: a name is unique within a run, and an artifact cannot be rewritten once finalized. The second upload is not overwriting the first and is not being merged into it; it is refused.

Three version 3 habits, three version 4 replacements. The uniqueness is per run, not per job, which is the part testing usually reveals last.
What this error means
One leg of a matrix uploads and the rest fail, or a second upload in the same job fails while the first passed. Whichever leg got there first survives, so the failing job changes between runs and the whole thing reads as flaky. The log above the error looks healthy: a file count, a valid name, a valid root directory. The failure line does not name the artifact it collided with, a gap reported against the action itself.
```Actions log, quoted from upload-artifact#634
Error: Failed to CreateArtifact: Received non-retryable error: Failed request: (409) Conflict: an artifact with this name already exists on the workflow run
## Common causes
### Every leg of a matrix uploads the same name
The dominant cause, and the one the migration document leads with. Under version 3 this was the normal way to collect output from several platforms into one artifact. Under version 4 it is a race, and which leg wins varies, which is why this is usually first reported as a flaky matrix rather than a duplicated name.
### The same name is uploaded twice in one run, in different jobs
Because uniqueness is per run rather than per job, splitting the uploads apart does not separate the names. Two jobs that both upload under `coverage` collide exactly as if they were two steps in one job, and moving one into its own job is the most common failed fix for this error.
### A shared or third-party action uploads a name you do not control
Composite and marketplace actions often upload a diagnostic bundle under a fixed name. Call one twice in a run and the second call fails inside somebody else's code. The Microsoft action cited below is exactly that: it offered no way to set the artifact name, so every workflow calling it twice broke.
### overwrite is set but is looking at the wrong name
On version 7, direct upload with `archive: false` derives the name from the file while `overwrite` is reported to still delete by the `name` input. The delete removes nothing, the create hits the existing artifact, and you get this error despite having asked for overwriting. The practical answer is to avoid combining those two inputs.
## How to fix it
### Put the matrix value in the name
The corrected version of the illustrative workflow above, and the change the migration document makes in its own diff. Every leg now uploads a distinct artifact and none races any other. It is the right fix for most matrices, because the artifacts really are different.
```.github/workflows/ci.yml, corrected (illustrative)
- uses: actions/upload-artifact@v7
with:
name: my-artifact-${{ matrix.os }}
path: file.txt
if-no-files-found: error
Download them back as one directory
Splitting the upload only moves the problem if the consumer still expects one artifact. The download side has two inputs for this: pattern selects the set by prefix and merge-multiple puts them in one directory instead of one subdirectory each. It pairs with the fix above as written.
```.github/workflows/ci.yml, corrected (illustrative)
collect:
needs: build
runs-on: ubuntu-latest
steps:
- uses: actions/download-artifact@v8
with:
path: all
pattern: my-artifact-*
merge-multiple: true
- run: ls -R all
### Merge into one real artifact when you need a single download
If something outside the workflow downloads the artifact, a set of names is not enough and you want one object. The `merge` sub-action does that in an extra job: it pulls the matching artifacts, combines them and uploads one. It costs a round trip of the data, which is why the pattern download is better when a later job is the only consumer.
```.github/workflows/ci.yml (illustrative)
merge:
needs: build
runs-on: ubuntu-latest
steps:
- uses: actions/upload-artifact/merge@v7
with:
name: all-my-files
pattern: my-artifact-*
Use overwrite only where last writer wins is what you mean
Republishing one artifact from one job, such as a preview build that should reflect the latest push within a run, is what this input was added for. Do not reach for it to silence a matrix: the legs delete one another and the survivor is whichever finished last. The replacement is a new artifact with a new id, so any link captured earlier goes stale.
```.github/workflows/ci.yml (illustrative)
- uses: actions/upload-artifact@v7 with: name: preview path: dist overwrite: true if-no-files-found: error ```
How to prevent it
- Derive every artifact name from the thing that makes it unique, usually the matrix value.
- Treat artifact names as unique across the run, not the job, when reviewing a workflow.
- Give any shared or composite action that uploads an artifact a name input.
- Reserve
overwritefor a single deliberate republish and never for a matrix.
A minimal workflow that produces it
This file is written for this page and has never been run. It is the shape the version 4 migration document uses as its own example of what stopped working: three matrix legs, each uploading its own file under one shared artifact name. Under version 3 that produced one artifact holding three files. Under version 4 the first leg creates it and the other two get a 409.
```.github/workflows/ci.yml (illustrative)
name: ci
on:
push:
jobs:
build:
strategy:
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]
runs-on: ${{ matrix.os }}
steps:
- run: echo "hello from ${{ matrix.os }}" > file.txt
- uses: actions/upload-artifact@v7
with:
name: my-artifact
path: file.txt
## What version 4 changed, and when each replacement arrived
Version 4 landed on 14 December 2023 as a rewrite of the artifact backend. Its release notes call it a major update with breaking changes, adding that artifacts created with version 3 and below are not compatible and that uploads and downloads must use the same major versions. Immutability is the piece that produces this error: the migration document says that in version 4 artifacts are immutable unless deleted.
The replacements did not all arrive at once, which is worth knowing if you are reading advice from early 2024 and wondering why an input does not exist. The dates below come from the release notes, checked today against the GitHub releases API.
Everything after version 4.4.0 is runtime and packaging rather than behavior. Version 5 added Node 24 support, version 6 made Node 24 the default and raised the minimum Actions runner to 2.327.1, and version 7 added direct uploads and moved to ESM. None of them changed how names collide.
| Version | Released | What it changed about names |
| --- | --- | --- |
| v4.0.0 | 14 December 2023 | Artifacts became immutable and names unique within a run |
| v4.2.0 | 18 January 2024 | Added `overwrite`, which deletes the old artifact first |
| v4.3.0 | 23 January 2024 | Added the `merge` sub-action for combining artifacts |
| v4.4.0 | 30 August 2024 | Excluded hidden files by default, unrelated to naming |
## Unique per run, not per job
The obvious reading of the message is that the collision is within a job, and it is not. The reporter of one widely linked case tested exactly that, splitting each upload into its own job, and reported back that it made no difference: the name has to be unique across the whole run. That is why moving a duplicated upload into a separate job does not help.
Two inputs sit either side of the rule. `overwrite: true` deletes any artifact with that name before uploading, which gives last-writer-wins, and the migration document adds the consequence that matters: this creates an entirely new artifact with a different id from the previous one. Set it on a matrix and the legs delete each other in whatever order they finish.
The other direction is the `merge` sub-action, which downloads a set of artifacts and re-uploads them as one, at the cost of an extra job and a round trip of the data. One open edge on version 7: with `archive: false` the name comes from the uploaded file rather than the `name` input, and a report says `overwrite` still deletes by `name`.
> A conflict is only one of the ways a create call fails: see [GitHub Actions artifact upload failed](https://latchkey.dev/learn/github-actions/gha-artifact-upload-failed-v4) for the rest.
## Why there is no recorded run on this page
A 409 here is a correct answer to a request for something the service does not allow. It is deterministic and documented as intended, and re-running produces it again in whichever leg loses the race. Nothing in it is transient and there is nothing for a runner to repair: the fix is a different name, and that lives in the workflow file.
## FAQ
### Why does upload-artifact say a name already exists in a matrix?
Because version 4 made artifacts immutable and names unique within a run, so only the first leg can create that name and the rest get a 409. Version 3 merged those uploads into one artifact, which is why this appears on the first run after an upgrade. Put the matrix value in the name.
### Are GitHub Actions artifact names unique per job or per run?
Per run. Moving a duplicated upload into its own job does not avoid the conflict, which is the most common failed fix for this error. One widely linked report worked through exactly that, splitting each upload into a separate job before concluding that the name has to be unique across the whole workflow run.
### What does overwrite: true do in actions/upload-artifact?
It deletes any existing artifact with that name before uploading the new one, so the last writer wins. The migration document adds the part that catches people: the result is an entirely new artifact with a different id, so anything that captured the old id or url now points at something gone. Available since version 4.2.0.
### How do I combine matrix artifacts into one artifact now?
Upload one name per leg, then add a job running the `merge` sub-action with a `pattern` matching them. It downloads the set, combines it and uploads one artifact under your name. If the only consumer is a later job in the same run, download by pattern with `merge-multiple` instead.
## References
- [The report quoting the 409 conflict line, filed against the action](https://github.com/actions/upload-artifact/issues/634)
- [A case establishing that artifact names are unique across a whole run](https://github.com/microsoft/powerplatform-actions/issues/625)
- [actions/upload-artifact migration notes: immutability, overwrite and merge](https://github.com/actions/upload-artifact/blob/main/docs/MIGRATION.md)
- [Release notes for version 4 of actions/upload-artifact](https://github.com/actions/upload-artifact/releases/tag/v4.0.0)
Top comments (0)