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
- 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.
- 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.
- 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).
Problem
The
Probe external link livenessstep inci.ymlpasses--accept-timeouts=true, and the job's own step summary states the intent plainly: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=truedoes not cover it and the job exits 2.Evidence
Run 30610933921, job
Lint docs links, on PR #320. The PR changed onlyscripts/__tests__/test-editor-provisioning-heartbeat.ps1and aprogress/markdown file; neither links to openupm.com.Final summary line:
Six timeouts were accepted. The single error that failed the gate was the same host, and specifically a cached result:
The identical URL is a
TIMEOUTat its first two occurrences and anERRORat 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 linksfeedsCI 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
excludefor openupm.com..lychee.tomlsays so explicitly, and it would hide a genuinely dead link later.accepttakes status codes, so it cannot express this case..lychee.tomlwithoutaccept_timeouts, so any change must keep the advisory scan reporting timeouts while the blocking gate keeps ignoring them.Candidate approaches
timeoutandmax_retriesin.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.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).