869 words
4 minutes

GitHub Actions upload-artifact 409: Fix “Artifact Already Exists”

2026-08-20
DevOps
GitHub Actions
/
CI/CD
/
DevOps
/
Troubleshooting

actions/upload-artifact can fail with 409 Conflict or an artifact with this name already exists when two uploads in the same workflow run target one artifact name. With the v4 artifact model, an artifact is immutable after creation unless it is explicitly deleted and replaced. The normal fix for a matrix is to make the name unique; overwrite: true is for a sequential replacement, not for combining parallel uploads.

Decide whether the outputs are separate or one bundle#

The right fix depends on what the artifact represents:

Workflow shapeCorrect design
Each matrix job produces an independent buildAdd the matrix value to name
Several jobs should become one downloadable archiveUpload unique artifacts, then merge them in a later job
A later job should replace an older snapshotUse overwrite: true after the earlier upload has finished
One job has several filesUpload a directory or a deliberate file set in one step

Do not give every parallel job name: build. The name is part of the artifact identity, so the jobs are racing to create the same object rather than appending files to a shared directory.

Give matrix jobs unique artifact names#

The most predictable matrix pattern is to include every value that changes the output:

jobs:
build:
strategy:
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]
node: [22, 24]
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v5
- uses: actions/setup-node@v5
with:
node-version: ${{ matrix.node }}
- run: npm ci
- run: npm run build
- uses: actions/upload-artifact@v4
with:
name: build-${{ matrix.os }}-node-${{ matrix.node }}
path: dist/

Use the values that actually distinguish the files. If the build varies by browser, architecture, feature flag, or package, include that dimension as well. A unique name prevents a conflict, but it also makes the artifact list useful when you need to compare outputs after a failed run.

The same rule applies when the upload uses archive: false. In that mode the uploaded file’s name determines the artifact name, so two jobs can still collide even when a name input appears in the step. Make the source filenames unique or keep the default archive behavior when you need an explicit artifact name.

Merge parallel artifacts after the matrix#

If the reader of the workflow needs one artifact containing files from every matrix job, do not make the matrix jobs write to one artifact. Upload separate artifacts first, then add a dependent merge job:

jobs:
build:
strategy:
matrix:
platform: [linux, macos, windows]
runs-on: ${{ matrix.platform }}-latest
steps:
- uses: actions/checkout@v5
- run: ./scripts/build-${{ matrix.platform }}.sh
- uses: actions/upload-artifact@v4
with:
name: package-${{ matrix.platform }}
path: out/
merge:
needs: build
runs-on: ubuntu-latest
steps:
- uses: actions/upload-artifact/merge@v4
with:
name: all-packages
pattern: package-*

The merge job must wait for the matrix. That ordering turns many immutable artifacts into one new artifact without asking parallel jobs to mutate shared state. Keep the names and pattern narrow enough that an unrelated artifact from another step cannot be pulled into the bundle.

Reserve overwrite for a finished artifact#

For a sequential workflow that intentionally publishes a fresh snapshot under the same name, use overwrite: true:

jobs:
upload-preview:
needs: build
runs-on: ubuntu-latest
steps:
- uses: actions/download-artifact@v4
with:
name: build-output
path: preview/
- uses: actions/upload-artifact@v4
with:
name: preview
path: preview/
overwrite: true

This replaces the previous artifact and creates a new artifact record. It does not merge files into the existing archive, and it is not a safe way to resolve simultaneous matrix uploads. If two jobs can reach the step at once, use unique names and a later merge instead.

Check the rendered workflow before changing the action#

When the error is not obvious, inspect the workflow as a set of jobs rather than changing action versions repeatedly:

  1. Search every actions/upload-artifact step and list its final name after expression expansion.
  2. Check matrix dimensions, reusable-workflow inputs, and job fan-out.
  3. Look for archive: false, because the file basename may become the identity.
  4. Confirm that a retry or rerun is not intentionally reusing a name that should be unique per run.
  5. Decide whether a separate artifact, a merge job, or a sequential replacement matches the consumer.

If the upload then fails because a file is absent, that is a different boundary. Set if-no-files-found: error for required output and use the existing GitHub Actions hidden-files guide when dot-prefixed build files are part of the intended artifact. Do not enable hidden-file uploads merely to hide a path mistake.

The useful distinction is simple: parallel jobs should create different artifact names, a merge job should assemble a single bundle, and overwrite should replace one completed snapshot in a controlled sequence.

FAQ#

Q: Why does a matrix upload fail when every job uses the same artifact name?#

A: GitHub Actions artifacts are immutable in the v4 model. The second job cannot append to the artifact created by the first, so the service returns a conflict. Add the matrix value to the name or upload unique artifacts and merge them later.

Q: Does overwrite: true combine files from multiple jobs?#

A: No. It deletes a matching artifact before uploading a new one. Use it only when the replacement is sequential and intentional; use unique names plus actions/upload-artifact/merge when the inputs come from parallel jobs.

Q: Is a missing dotfile the same problem as an existing-artifact conflict?#

A: No. Hidden-file exclusion controls which paths are included in an upload. An existing-artifact conflict means the final artifact identity is already taken. Diagnose the path contents and the artifact name separately.

References:

actions/upload-artifact README

actions/upload-artifact migration guide

actions/upload-artifact issue #769

GitHub Actions upload-artifact 409: Fix “Artifact Already Exists”
https://laplusda.com/en/posts/github-actions-upload-artifact-name-conflict/
Author
Zero
Published at
2026-08-20
License
CC BY-NC-SA 4.0
Was this article useful?

Report a typo or broken link, or suggest a related topic.