GitHub Actions · beginner

GitHub Actions artifact not found: trace the producer-consumer run boundary

A GitHub Actions artifact not found error almost always signals a boundary mismatch between the producing run and the consuming run or job. The artifact, the run id, or the reference used to fetch it is anchored to a different execution context than the consumer expects. Treat this as a producer/consumer boundary problem before changing upload or download steps.

The symptoms

  • actions/download-artifact step fails with "Unable to find an artifact with the name '...'" or "No artifact was found for the name: ..."
  • actions/upload-artifact succeeds locally but the next job reports the artifact is missing from the workflow run
  • Download returns artifacts but the contents are empty or refer to a previous run id, not the current one
  • Upload step reports a successful upload yet the consumer job cannot see any artifact at all in the run summary
  • Error references a different run id or workflow than the one that just produced the upload

Likely causes

  • Producer job was skipped, cancelled, or failed before the upload-artifact step executed, so nothing was registered for the run
  • Consumer job runs in a different workflow_run that is not the same execution, so the artifact name and run id do not match
  • needs context is missing or misnamed, so the consumer does not wait for the producer and races against upload completion
  • Conditional logic (if:) resolved to false on the upload step, silently skipping the upload while the consumer assumes it ran
  • An earlier job with the same name in the workflow re-uploaded an artifact and overwrote what the consumer expected
  • artifact name drifted between producer and consumer due to a path expression or matrix expansion that evaluates differently per runner

First ten minutes

  1. 01Open the failing workflow run summary and read the run id from the URL or top-of-page header; note it as the consumer's run id
  2. 02Open the Artifacts section at the bottom of the run summary and confirm whether any artifact is listed for the consumer's run id
  3. 03Inspect the producer job's status icon; if it is skipped, cancelled, or failed, the upload step never executed and no artifact exists for this run
  4. 04Compare the workflow YAML referenced by the producer step against the consumer's needs: declaration to confirm the producer job name matches exactly
  5. 05Read the failing download-artifact step's log and capture the artifact name, the run id it is resolving against, and the workflow file path it reports
  6. 06Decide the boundary: same-run cross-job vs cross-run cross-workflow, since the resolution rule differs between the two

Evidence to collect

  • Run id of the consumer run, taken from the GitHub Actions URL or run summary header
  • Producer job name as declared in the workflow YAML and as referenced in the consumer's needs: block
  • Artifact name string exactly as passed to both actions/upload-artifact and actions/download-artifact steps, including any expression interpolation result
  • Upload step status (success, skipped, failed) and the resolved value of any if: condition guarding it
  • Workflow file path and ref (branch or tag) the consumer run was triggered from, compared to the producer run's ref
  • Run-summary Artifacts list for the consumer's run id, showing whether the artifact is present, absent, or attached to a different run

Where to look

  • At the workflow run summary page's Artifacts section, which is the authoritative listing of artifacts owned by that specific run id
  • At the producer job's step list, specifically the upload-artifact step log lines showing the resolved name and the upload target run
  • At the consumer job's needs: block in the workflow YAML, since needs gates both job ordering and artifact resolution across jobs in the same run
  • At the d.yaml or workflow file referenced by the failing download-artifact error, to confirm the consumer is looking at the expected workflow definition
  • At the run's metadata fields (event, ref, run attempt), which determine how run ids and artifact scoping interact across retries and re-runs

Diagnostic steps

  1. 01Confirm the consumer's run id equals the producer's run id; if they differ, you are looking at a cross-run reference and must treat the producer as a separate execution
  2. 02Verify the producer job's needs relationship is satisfied (status success or specified condition) before the consumer is allowed to run
  3. 03Compare the artifact name string byte-for-byte between upload and download steps, including dynamic segments resolved at runtime
  4. 04Inspect the upload step's resolved if: condition in the workflow log to confirm the upload was not skipped at runtime
  5. 05Check whether the producing workflow uses a matrix or reusable workflow that may emit multiple artifacts under the same name on parallel runners
  6. 06For cross-workflow consumers, confirm the download step targets the correct workflow name and run id, not just the artifact name
  7. 07Rule out run-attempt drift: artifacts from a previous attempt are not automatically visible to a new attempt of the same job

Common mistakes

  • Assuming the artifact exists because the upload step reported success, without checking the run-summary Artifacts list for the actual run id
  • Using the same artifact name across two unrelated workflows and expecting the consumer to find the latest upload globally rather than the specific run it references
  • Adding a new needs: entry but forgetting that GitHub Actions resolves artifacts only along declared needs dependencies, not by job name proximity
  • Interpolating the artifact name from a variable that expands differently on the upload runner versus the download runner, so the names silently diverge
  • Re-running only the consumer job instead of the full workflow, which can cause the download step to resolve against an earlier run attempt's artifact scope

Safe fixes

  • If the producer was skipped or failed, fix the upstream condition so the upload step is actually reachable before changing the consumer
  • If the consumer run id differs from the producer run id, switch the download step to a cross-run reference that explicitly names the producer workflow and run id
  • If needs: is missing or misnamed, add the producer job to the consumer's needs: block so artifact resolution is scoped to the in-run dependency
  • If the artifact name is dynamic, centralize the name in a workflow-level output or env and reference it from both producer and consumer steps to avoid drift
  • If matrix expansion causes name collisions, append a matrix identifier to the artifact name on the producer so each leg produces a uniquely named artifact

Prove the fix

  1. 01Re-run the workflow and confirm the consumer's run summary Artifacts list shows the expected artifact name under the same run id as the producer
  2. 02Grep the consumer job's download-artifact log for the resolved run id and confirm it matches the producer's run id shown in the run summary header
  3. 03Verify the consumer's needs: block lists the producer job and that the producer job's status is success in the run graph before the consumer starts
  4. 04Inspect the uploaded artifact's contents in the run summary and confirm the files inside match what the consumer step expected to read
  5. 05Repeat the re-run at least once and confirm the artifact still resolves correctly, ruling out a one-time match from a stale run attempt

Prevention and next steps

  • Keep artifact names static and defined once at the workflow level, referenced from both producer and consumer steps, so name drift cannot occur between runs
  • Always declare the producer job in the consumer's needs: block, even when the consumer only depends on the artifact and not the producer's outputs
  • Add an explicit assertion step early in the consumer that fails fast if the expected artifact name is missing, rather than relying on the download step's generic error
  • Avoid conditionally skipping the upload step with the same name pattern used elsewhere; prefer distinct names per producer to keep consumption deterministic