# The CI Artifact Link Expired — Recovering the Report, and Not Losing the Next One

Canonical: https://commareports.com/fix/ci-artifact-expired
Published: 2026-09-07

> Artifact retention silently 404s every report link you have ever pasted into a PR or an incident doc. What you can still recover, what you can't, and how to make the link permanent.

# The artifact link in the PR is a 404

Someone opens a six-month-old incident review, clicks the link to the
failing test report, and gets a 404. Or a code review resumes after a
long weekend and the coverage artifact is gone. The link was never
permanent; it just hadn't expired yet when it was pasted.

## What actually deleted it

Retention is only one of four mechanisms, and it is often not the one
that got you.

| Mechanism              | Where                                | What it does                                                    |
| ---------------------- | ------------------------------------ | --------------------------------------------------------------- |
| Time-based retention   | GitHub 90d, GitLab 30d, CircleCI 30d | Deletes by age, regardless of whether anyone is reading it      |
| Keep-latest-successful | GitLab                               | Deletes superseded pipelines' artifacts immediately, at any age |
| Build discarders       | Jenkins, TeamCity                    | Deletes by build **count** — ten green builds erase build #1    |
| Visibility change      | Any provider                         | Repo goes private, and every anonymous link stops resolving     |

The count-based ones are the surprise. A busy repository can retire an
artifact within a day.

## Can you get it back?

From the provider, no. Retention deletion is permanent — there is no
undelete on any major CI provider, and support cannot restore it.

The only recovery is re-running the job, which is a real option and
often a mirage:

- The commit still exists, but the runner image has moved on.
- `npm install` resolves different transitive versions than it did then.
- The scanner's vulnerability database has been updated, so the report
  you regenerate is not the report you lost.
- The external service the test hit has changed its responses.

For anything you might need to cite later — a compliance scan, an
incident timeline, a benchmark you claimed a regression against — a
regenerated report is a different document. It is evidence with a new
date on it.

## Making the next one permanent

Separate the two things CI artifacts are being asked to do:

**Machine artifacts** — caches, build outputs, files a downstream job
downloads. Keep uploading these. Retention is correct for them, and
shorter is better.

**Documents** — test reports, coverage, scans, benchmarks, anything a
person is meant to read. Publish these to an address you own:

```bash
# once, by hand: create the report, save the id as a CI variable
# every run thereafter:
curl -sS -X PATCH "https://commareports.com/api/v1/reports/$COMMA_REPORT_ID" \
  -H "Authorization: Bearer $COMMA_API_TOKEN" \
  -H "Content-Type: application/json" \
  --data @<(jq -Rs '{html: .}' htmlcov/index.html)
```

One id, one URL, for the life of the project. Each run appends a
revision rather than overwriting, so the history is browsable and any
two builds can be diffed — which is the thing artifact retention was
destroying. Setup per provider is in [publish from CI](/docs/ci).

## Why this changes review, not just storage

A permanent link is also a place to put the conversation. Reviewers
comment on the failing assertion inside the report instead of quoting it
into a chat thread that scrolls away
([comment on HTML](/comment-on-html)), and the thread stays attached
across revisions — so "we decided this flake was environmental" is still
readable next to the test six months later.

## Try it

Comma is free — unlimited reports, unlimited commenters, unlimited
revision history. Nothing expires.

**[Create a permanent report URL →](https://commareports.com/)**

### Related

- [GitHub Actions artifact HTML won't open](/fix/github-actions-artifact-html-not-viewable)
- [Publish from CI](/docs/ci) · [GitLab CI HTML reports](/ci/gitlab-ci-html-report)
- [Stop screenshotting reports](/stop-screenshotting-reports)
