Artifacts, Retention, Cross-Job Data, and Workflow Result Management: Diagnostics, Failure Modes, and Production Practices
Artifact failures become dangerous when teams troubleshoot by re-uploading, renaming, or deleting evidence before proving what the first run produced. This lesson diagnoses cache misuse, sensitive-file leakage, expiry assumptions, matrix collisions, run/SHA confusion and deprecated action majors while preserving first-failure evidence.
Learning objectives
- Diagnose missing/wrong artifacts by preserving run/attempt/SHA and artifact metadata before repair.
- Recognize sensitive-file leakage, cache misuse, expiry assumptions and matrix name collisions as different failure layers.
- Treat downloaded artifacts as untrusted until their producing run/SHA and expected content are verified.
- Identify deprecated/unsupported artifact action majors as platform compatibility failures rather than application failures.
- Apply the least destructive repair and rerun only the smallest equivalent scope.
1. Evidence-first diagnostic sequence
- Preserve the run ID, attempt, event, ref/SHA and workflow revision.
- Record the generated job graph and conclusions before rerun/cancellation changes them.
- Record artifact list/IDs/names/digests/sizes/expiry for that run.
- Confirm runner/image and exact pinned action SHAs.
- Compare configured upload paths with pre-upload filesystem evidence.
- Inspect download selection: name, ID, source run/repository and destination path.
- Inspect sensitivity/hidden-file policy and whether any artifact should be quarantined/deleted.
- Apply the narrowest fix, create a new run/attempt, and keep the original evidence.
2. Symptom → likely layer → evidence
| Symptom | Likely cause | Preserve first | Least-destructive correction |
|---|---|---|---|
| Release job restored stale files | cache used as authoritative build evidence | cache key/hit + source SHA + release input | use artifact tied to producing run/SHA; keep cache only for dependencies |
| Secret-like file visible in artifact | path selection/data classification failure | artifact ID/digest + exact selected paths + access history | revoke credential, restrict access, delete/quarantine artifact after evidence, fix path selection |
| Artifact disappeared | retention expiry or run deletion | REST metadata if available + repo retention policy + run status | increase future retention or promote records externally; cannot “repair” expired bytes from metadata alone |
| Matrix upload says artifact already exists | same-name collision under immutable semantics | matrix cell values + upload logs | unique artifact name per cell; aggregate later |
| Consumer has valid artifact but wrong build | wrong run/name selection | source run ID, head SHA, artifact ID/digest | select by expected run/ID and verify SHA before use |
| Action unsupported/failing before upload | deprecated/incompatible artifact action/backend | action major/SHA + GitHub.com/GHES version | use supported major for target platform; do not downgrade blindly |
3. Intentionally broken example: name-only selection finds no artifact
The producer uploads build-${{ github.run_id }}, while
the consumer assumes the name is simply build. The
download failure is not a runner or network problem; the selection
contract is wrong.
# Producer
- id: upload
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a
with:
name: build-${{ github.run_id }}
path: dist/build.txt
# Broken consumer: wrong human name
- uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c
with:
name: build
path: ${{ runner.temp }}/incoming
Preserve the failed run and upload output. Repair the dataflow by
promoting artifact-id to a job output and selecting
artifact-ids in the consumer. This removes hidden
coupling to naming convention.
# Repaired consumer selection
with:
artifact-ids: ${{ needs.build.outputs.artifact_id }}
path: ${{ runner.temp }}/incoming
digest-mismatch: error
4. Sensitive artifact response is an incident, not a cosmetic bug
If a real credential or private key is uploaded, deleting the artifact is not sufficient. Assume authorized readers or automation may already have downloaded it. Preserve incident metadata, revoke/rotate the credential, inspect access/audit evidence where available, then remove the artifact and fix the workflow selection. Never publish the secret in a ticket or log “to prove” the leak.
GitHub log redaction does not encrypt artifact contents. Base64 is reversible encoding, not protection. Secrets, token-bearing headers and private keys must not be artifact inputs.
5. Matrix collisions are evidence loss
Under current immutable artifact semantics, multiple matrix cells
cannot all upload to the same artifact name as if appending. Give
every cell a unique name, for example
test-${{ matrix.os }}-${{ matrix.runtime }}-${{ github.run_id
}}. If you want one combined object, use a downstream aggregation
step after all cells complete.
6. Expiry is not transient failure
An expired artifact download can return Gone/404-like outcomes
depending on interface. Retrying will not resurrect it. Check
expired/expires_at and the repository
retention policy. Rebuilding a release artifact merely to make the
error disappear breaks provenance because the new bytes are produced
by a new run; treat it as a new build with a new identity.
7. Read metadata before delete
GH_REPO='OWNER/gha-artifact-lab'
ARTIFACT_ID='123456789'
# Preserve metadata first
gh api -H 'X-GitHub-Api-Version: 2026-03-10' "repos/$GH_REPO/actions/artifacts/$ARTIFACT_ID" --jq '{id,name,size_in_bytes,digest,expired,created_at,expires_at,workflow_run}'
# Destructive cleanup only after evidence/authorization
# gh api --method DELETE .../actions/artifacts/$ARTIFACT_ID
Knowledge check
A consumer downloaded a valid-digest artifact from the wrong run. Is integrity sufficient?
No. Integrity of the wrong object is still wrong input. Bind the artifact to the intended source run/SHA.
What is the first response to a real secret found in an artifact?
Treat it as credential exposure: preserve incident metadata, revoke/rotate, assess access, then remove/quarantine and fix selection.
Why is retrying an expired artifact download not useful?
Expiry is lifecycle state, not a transient network error. The bytes have been removed from artifact storage.
Why should matrix jobs use unique artifact names?
Immutable artifacts do not support multi-job append into one same-name record; unique names preserve per-cell evidence and avoid collisions.
Why not rebuild a missing release artifact as a troubleshooting shortcut?
A rebuild creates new provenance, run ID, environment and potentially different bytes. It cannot silently stand in for the original release evidence.
Official references and version notes
- GitHub Docs — Workflow artifacts — artifact purpose, cache distinction and attestation context.
- GitHub Docs — Store and share data with workflow artifacts — upload/download, retention, cross-job transfer and digest validation.
- GitHub REST API — Actions artifacts — artifact ID, size, digest, expiration, download and delete endpoints.
-
actions/upload-artifact v7.0.1
— upload action pinned to
043fb46d1a93c77aae656e7c1c64a875d1fc6a0a. -
actions/download-artifact v8.0.1
— download action pinned to
3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c. - GitHub Docs — Artifact attestations — signed provenance is separate from ordinary artifact storage/digest verification.
Version-sensitive behavior was rechecked on
2026-09-09. Mandatory examples target GitHub.com
and ubuntu-24.04.
actions/upload-artifact v7.0.1 and
actions/download-artifact v8.0.1 are pinned by full
commit SHA. Upload v7 excludes dot-prefixed hidden files by
default, supports compression levels 0–9, returns artifact
ID/URL/SHA-256 digest, and treats overwrite as delete-and-create
rather than in-place mutation. Download v8 can select by artifact
ID and defaults digest mismatch handling to error.
Artifact/log retention is policy bounded: GitHub documents 1–90
days for public repositories and up to 400 days for private
repositories when repository/organization/enterprise policy
permits. Upload-artifact v4+ is not supported on GitHub Enterprise
Server; GHES users must use an appliance-compatible artifact
action/backend and must not copy GitHub.com majors blindly.
Keep the academy open
Support free, practical DevOps education.
Every lesson is designed to remain readable in a browser, downloadable from GitHub, and usable without a paid learning platform. Contributions help expand and maintain the curriculum.
0x716c4Ab160C4B66F31a28AE2448BfF68fc3a2ef0Send only Ethereum/ERC-20 compatible assets to this
address.