Skip to content

Fix: GitHub Actions Artifacts Not Working — Upload Fails, Download Empty, or Artifact Not Found

FixDevs ·

Quick Answer

How to fix GitHub Actions artifact issues — upload-artifact path patterns, download-artifact across jobs, retention days, artifact name conflicts, and the v3 to v4 migration.

The Problem

upload-artifact completes but the artifact is empty or not found:

- uses: actions/upload-artifact@v4
  with:
    name: build-output
    path: dist/
# "Warning: No files were found with the provided path: dist/"

Or download-artifact in a later job fails:

download-job:
  needs: build-job
  steps:
    - uses: actions/download-artifact@v4
      with:
        name: build-output
    # Error: Unable to find artifact 'build-output'

Or artifact names conflict and overwrite each other in a matrix build:

strategy:
  matrix:
    os: [ubuntu-latest, windows-latest]
steps:
  - uses: actions/upload-artifact@v4
    with:
      name: test-results  # Same name for all matrix jobs — conflict
      path: results/

Why This Happens

Artifacts in GitHub Actions have several non-obvious behaviors:

  • Paths are relative to the workspacepath: dist/ refers to $GITHUB_WORKSPACE/dist/. If your build step runs in a different directory or the output path differs, no files match.
  • Artifacts are scoped to the workflow run — a job can only download artifacts uploaded in the same workflow run, unless you use the run-id parameter.
  • Matrix jobs share the artifact namespace — if two matrix jobs upload artifacts with the same name, one overwrites the other (in v3) or the upload fails (in v4 with overwrite: false).
  • v3 vs v4 breaking changesactions/upload-artifact@v4 and actions/download-artifact@v4 have different defaults and no longer support downloading all artifacts without a name.

Fix 1: Verify the Upload Path Exists

The most common cause of empty artifacts is the path not existing at upload time:

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Build
        run: npm run build  # Outputs to dist/

      # Debug: list what's in the workspace
      - name: List files
        run: ls -la dist/ || echo "dist/ does not exist"

      - uses: actions/upload-artifact@v4
        with:
          name: build-output
          path: dist/
          if-no-files-found: error  # Fail the step if nothing matched (default: warn)

Common path issues:

# WRONG — path inside a subdirectory, but workspace is the root
- run: cd myapp && npm run build  # Output goes to myapp/dist/

- uses: actions/upload-artifact@v4
  with:
    path: dist/  # Looks for $GITHUB_WORKSPACE/dist/ — wrong!

# CORRECT — include the subdirectory in the path
- uses: actions/upload-artifact@v4
  with:
    path: myapp/dist/

# Or set working-directory for the build step:
- name: Build
  working-directory: myapp
  run: npm run build

- uses: actions/upload-artifact@v4
  with:
    path: myapp/dist/  # Still relative to GITHUB_WORKSPACE

Use glob patterns to capture multiple files:

- uses: actions/upload-artifact@v4
  with:
    name: test-results
    path: |
      coverage/
      test-results/*.xml
      **/*.log
    if-no-files-found: error

Fix 2: Download Artifacts Correctly Across Jobs

Artifacts are only available after the uploading job completes. Use needs: to declare the dependency:

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - run: npm run build
      - uses: actions/upload-artifact@v4
        with:
          name: build-output
          path: dist/

  deploy:
    runs-on: ubuntu-latest
    needs: build  # Waits for 'build' to complete
    steps:
      - uses: actions/download-artifact@v4
        with:
          name: build-output
          path: dist/  # Downloads to this directory

      - name: Deploy
        run: |
          ls dist/  # Verify download succeeded
          ./deploy.sh

Download all artifacts from the run:

# actions/download-artifact@v4 — download all artifacts
- uses: actions/download-artifact@v4
  # No 'name' parameter — downloads all artifacts
  # Each artifact goes into a subdirectory named after the artifact

- name: List downloaded artifacts
  run: ls -la  # Shows: build-output/, test-results/, etc.

Download an artifact from a different workflow run:

- uses: actions/download-artifact@v4
  with:
    name: build-output
    run-id: ${{ github.event.workflow_run.id }}  # Specific run ID
    github-token: ${{ secrets.GITHUB_TOKEN }}

Fix 3: Fix Matrix Build Artifact Name Conflicts

In matrix builds, each job must upload to a unique artifact name:

# WRONG — all matrix jobs overwrite the same artifact
jobs:
  test:
    strategy:
      matrix:
        os: [ubuntu-latest, windows-latest, macos-latest]
    runs-on: ${{ matrix.os }}
    steps:
      - run: npm test
      - uses: actions/upload-artifact@v4
        with:
          name: test-results  # Same name — conflict in v4, overwrite in v3
          path: results/

# CORRECT — unique name per matrix combination
jobs:
  test:
    strategy:
      matrix:
        os: [ubuntu-latest, windows-latest, macos-latest]
    runs-on: ${{ matrix.os }}
    steps:
      - run: npm test
      - uses: actions/upload-artifact@v4
        with:
          name: test-results-${{ matrix.os }}  # Unique per job
          path: results/

  merge-results:
    needs: test
    runs-on: ubuntu-latest
    steps:
      - uses: actions/download-artifact@v4
        with:
          pattern: test-results-*  # Download all matching artifacts
          merge-multiple: true      # Merge into a single directory
          path: all-results/

Fix 4: Migrate from v3 to v4

actions/upload-artifact@v4 and actions/download-artifact@v4 have breaking changes from v3:

# v3 — download all artifacts merges into current directory
- uses: actions/download-artifact@v3
  # Downloads all artifacts, potentially overwriting files with same names

# v4 — download all artifacts creates subdirectories
- uses: actions/download-artifact@v4
  # Each artifact in its own subdirectory named after the artifact

# v3 — artifact names could be duplicated across matrix jobs (last writer wins)
# v4 — duplicate artifact names fail by default
- uses: actions/upload-artifact@v4
  with:
    name: my-artifact
    path: output/
    overwrite: true  # Allow overwriting — restores v3 behavior

Key v4 changes:

# v4: 'path' when downloading now specifies the destination directory
- uses: actions/download-artifact@v4
  with:
    name: build-output
    path: downloaded/  # Files go into downloaded/

# v4: use 'pattern' for wildcard matching (replaces downloading without 'name')
- uses: actions/download-artifact@v4
  with:
    pattern: build-*       # Match multiple artifact names
    merge-multiple: false  # Each artifact in its own subdirectory (default)

Fix 5: Set Retention and Size Limits

Artifacts have default retention and size limits that cause unexpected expiry or upload failures:

- uses: actions/upload-artifact@v4
  with:
    name: large-build
    path: dist/
    retention-days: 7   # Keep for 7 days (default: 90 for public, varies for private)
    compression-level: 9  # 0-9, higher = smaller file, slower upload (default: 6)

Size limits by plan:

  • Free: 500 MB per artifact, 1 GB total per run
  • Pro/Team: 2 GB per artifact
  • Enterprise: configurable

Reduce artifact size:

# Exclude unnecessary files
- uses: actions/upload-artifact@v4
  with:
    name: build-output
    path: |
      dist/
      !dist/**/*.map     # Exclude source maps
      !dist/**/*.test.*  # Exclude test files
    compression-level: 9

Fix 6: Use Artifacts for Job Communication

Artifacts are the standard way to pass data between jobs in a workflow:

jobs:
  generate-matrix:
    runs-on: ubuntu-latest
    outputs:
      matrix: ${{ steps.set-matrix.outputs.matrix }}
    steps:
      - id: set-matrix
        run: |
          MATRIX=$(cat config/test-matrix.json)
          echo "matrix=$MATRIX" >> $GITHUB_OUTPUT

  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - run: npm ci && npm run build
      - uses: actions/upload-artifact@v4
        with:
          name: dist
          path: dist/
          retention-days: 1  # Short-lived — only needed for this run

  test:
    needs: build
    runs-on: ubuntu-latest
    strategy:
      matrix:
        shard: [1, 2, 3, 4]
    steps:
      - uses: actions/checkout@v4
      - uses: actions/download-artifact@v4
        with:
          name: dist
          path: dist/
      - run: npm test -- --shard=${{ matrix.shard }}/4
      - uses: actions/upload-artifact@v4
        with:
          name: test-results-shard-${{ matrix.shard }}
          path: test-results/
          if-no-files-found: error

  report:
    needs: test
    runs-on: ubuntu-latest
    steps:
      - uses: actions/download-artifact@v4
        with:
          pattern: test-results-shard-*
          merge-multiple: true
          path: all-test-results/
      - name: Generate report
        run: npx junit-merge all-test-results/**/*.xml -o combined.xml
      - uses: actions/upload-artifact@v4
        with:
          name: test-report
          path: combined.xml
          retention-days: 30

Still Not Working?

Artifact not found in a workflow_run event — when a triggered workflow tries to download artifacts from the parent workflow using the workflow_run event, you need to pass github-token and run-id explicitly. The artifact is only accessible after the parent workflow completes.

“Artifact storage quota has been reached” — your repository or organization has hit the artifact storage limit. Reduce retention-days for existing artifacts, delete old artifacts via the GitHub API (DELETE /repos/{owner}/{repo}/actions/artifacts/{artifact_id}), or increase the retention policy for your plan. Set short retention-days on temporary artifacts to avoid accumulation.

Artifact upload succeeds but shows 0 bytes — this happens when the path matches a directory structure but the files inside are empty, or when a previous step’s output was to stderr (and the files weren’t created). Add a run: ls -la <path> step before the upload to confirm files exist and have content.

Windows path separator issues — on Windows runners, use forward slashes in path: values even though Windows uses backslashes. GitHub Actions normalizes paths, but mixing separators can cause matching failures:

# Works on all platforms
path: build/output/  # Use forward slashes

# Avoid
path: build\output\  # May not work on Linux/macOS agents

For related CI/CD issues, see Fix: GitHub Actions Cache Not Working and Fix: GitHub Actions Timeout.

F

FixDevs

Solo developer based in Japan. Every solution is cross-referenced with official documentation and tested before publishing.

Was this article helpful?

Related Articles