Skip to content

ci: a cached link timeout is reported as an error, defeating --accept-timeouts #324

Description

Problem

The Probe external link liveness step in ci.yml passes --accept-timeouts=true, and the job's own step summary states the intent plainly:

Bot-detection, throttling, transient responses, and timeouts are accepted by this gate; do not add per-domain excludes for those cases.

That intent does not hold for a URL that appears more than once. When lychee times out on a URL and then reuses that result for a later occurrence of the same URL, the reused entry is reported as a generic error rather than a timeout, so --accept-timeouts=true does not cover it and the job exits 2.

Evidence

Run 30610933921, job Lint docs links, on PR #320. The PR changed only scripts/__tests__/test-editor-provisioning-heartbeat.ps1 and a progress/ markdown file; neither links to openupm.com.

Final summary line:

1714 Total (in 3m 43s 247ms)  581 Unique  153 OK  1 Error  1554 Excluded  6 Timeouts  15 Redirects

Six timeouts were accepted. The single error that failed the gate was the same host, and specifically a cached result:

[docs/getting-started/install.md]:
[TIMEOUT] https://openupm.com/packages/com.wallstop-studios.dxmessaging/ (at 20:47) | Request timed out
[TIMEOUT] https://openupm.com/packages/com.wallstop-studios.dxmessaging/ (at 33:51) | Request timed out

[README.md]:
[ERROR] https://openupm.com/packages/com.wallstop-studios.dxmessaging/ (at 15:1) | Error (cached)

The identical URL is a TIMEOUT at its first two occurrences and an ERROR at its third. Same host, same cause, same run; the classification differs only by cache hit, and only the cached one is fatal.

The earlier offline pass in the same job reported 0 Errors, so nothing internal was wrong.

Impact

Any PR can be turned red by a slow external host, regardless of what it changed, and the failure is a required check (Lint docs links feeds CI Success). The current remedy is a manual re-run, which is exactly the flake-tolerating loop the repository's testing policy rejects. It also trains reviewers to re-run a red required check without reading it, which is worse than the flake.

Constraints on any fix

  • Do not add a per-domain exclude for openupm.com. .lychee.toml says so explicitly, and it would hide a genuinely dead link later.
  • accept takes status codes, so it cannot express this case.
  • The scheduled advisory scan deliberately shares .lychee.toml without accept_timeouts, so any change must keep the advisory scan reporting timeouts while the blocking gate keeps ignoring them.

Candidate approaches

  1. Retry the external probe step once before failing, in the step's own shell. A second attempt repopulates the cache from a fresh request, so a transient host outage stops being fatal without weakening what the gate detects. Costs one extra pass on an already-failing run only.
  2. Raise timeout and max_retries in .lychee.toml (currently 20 s and 2). This reduces how often a host times out at all, but does not fix the misclassification, so a slow-enough host still produces a fatal cached error.
  3. Check whether lychee v0.24.2 preserves timeout status through its cache in a newer release, and bump the pinned version if so. This is the only option that fixes the cause rather than the symptom.

Option 3 first; option 1 if upstream still reclassifies.

Reproduction

Not deterministic on demand, since it needs the external host to be slow. It requires only that a URL time out once and appear at least twice across the checked docs, which https://openupm.com/packages/com.wallstop-studios.dxmessaging/ does (README.md plus docs/getting-started/install.md).

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions