Lesstep
GitHub Actions · actions/download-artifact · Checked November 4, 2026

GitHub Actions: "Artifact download failed after 5 retries"

actions/download-artifact found the artifact and could not get it onto the runner. It tries five times, five seconds apart, and writes the reason for each failure only to the debug log (download-artifact.ts in the toolkit). So the line you see is the same whatever went wrong. Two things sort it out: whether the same artifact fails again on a re-run, and the reason in the debug log.

What it looks like

Error: Unable to download artifact(s): Unable to download and extract artifact: Artifact download failed after 5 retries.

Which case is it

Re-run the failed job and tick Enable debug logging in the dialog, or set the repository variable ACTIONS_STEP_DEBUG to true. Each attempt then ends with its reason:

##[debug]Failed to download artifact after 1 retries due to Not a valid zip file. Retrying in 5 seconds...

1. The artifact is not a zip

docker/build-push-action and docker/bake-action upload a build record for each build, an artifact named like owner~repo~GQJ6I2.dockerbuild, and Docker's toolkit uploads it as a plain file, not a zip (skipArchive: true in artifact.ts). download-artifact before v8 tries to unzip every artifact it downloads. A step with no name and no pattern downloads every artifact of the run, so a release job that only wanted the binaries picks up the record too.

It can pass for a while first. The step lists only the artifacts that exist when it starts, and AuroraTape's release job had run before the Docker job finished for two releases. For 1.0.2-rc.1 the Docker job finished first, and no release was created (aurora-ltfs #271). The same in pwget_rs, porcupin and novastor (#4, #44, #222). Any one of these fixes it:

download-artifact@v8 on its own stops the failure but not the problem. v8 checks the Content-Type first and saves a file that is not a zip as it is (v8.0.0 release notes), so the record lands in your folder, and with merge-multiple: true a step that attaches release/* attaches it too. Keep the pattern.

The same happens to any file uploaded with archive: false (upload-artifact v7, a single file, uploaded unzipped) when a v4 to v7 download-artifact reads it. Use v8 on the downloading side.

2. A zip inside a zip

Playwright's blob reporter writes each shard's report as a zip file, and upload-artifact zips the folder again. download-artifact unzips while it streams, with unzip-stream, and two projects traced their failures to it: the inner zip's bytes can make it stop early, and the download fails with "unexpected end of file" on every attempt and every re-run. unzip -t, Python's zipfile and gh run download read the same artifact without trouble (kody #4748, EinVault #389). It depends on the bytes, so one shard fails and the next does not. kody's started with one commit and failed on every run on main after it.

EinVault uploads the shard's zip as it is and downloads it without unzipping. When sharded, Playwright names the file report-<hash>-<shard>.zip, and with archive: false the artifact takes the file's name.

# each shard
- uses: actions/upload-artifact@v7
  with:
    path: blob-report/*.zip
    archive: false

# the merge job
- uses: actions/download-artifact@v8
  with:
    pattern: report-*.zip
    skip-decompress: true
    path: all-blob-reports
    merge-multiple: true

kody instead downloads the blob reports with gh run download, which reads the zip's directory at the end of the file. That step needs a GH_TOKEN.

- run: gh run download "$GITHUB_RUN_ID" --pattern 'playwright-blob-report-*' --dir blobs
  env:
    GH_TOKEN: ${{ github.token }}

3. The storage, once

If the re-run passes, nothing in the workflow is wrong. tmt hit it twice in ten minutes, once in a release and once in a merge group (tmt #5743). v8.0.2 retries better when the service answers 429 and honors Retry-After (release notes), so use v8.0.2 or later. Where re-running the whole job costs a lot, as in a release, try a second time in the same job:

- id: dl
  uses: actions/download-artifact@v8
  continue-on-error: true
  with:
    name: build
    path: dist
- if: steps.dl.outcome == 'failure'
  run: sleep 60
- if: steps.dl.outcome == 'failure'
  uses: actions/download-artifact@v8
  with:
    name: build
    path: dist

A second try does not help sections 1 and 2: the same bytes fail the same way.

Telling it apart

See failed jobs from IntelliJ IDEA

Pipeline Inbox for GitHub Actions Workflows, a plugin by Lesstep for JetBrains IDEs 2024.1 or later, lists the runs of all your repositories in an IDE tool window, with their jobs and steps, and your current branch's runs are in the status bar. Reading is free; re-running failed jobs (with debug logging if you like), cancelling, running workflows and reviewing deployments are Pro (US$1.50 a month, with a 30-day trial).

GitHub and GitHub Actions are trademarks of GitHub, Inc.; Docker is a trademark of Docker, Inc.; IntelliJ IDEA is a trademark of JetBrains s.r.o. This page is not affiliated with, sponsored or endorsed by GitHub, Inc., Docker, Inc. or JetBrains.

More: "Artifact not found for name" · "Artifact storage quota has been hit" · Docker Hub's pull rate limit · GitHub Actions plugins for IntelliJ IDEA compared · 日本語