Skip to content

fix(links): stop GitHub 503s from filing false broken-link issues - #83

Merged
alexander-sei merged 3 commits into
mainfrom
fix/link-check-github-false-positives
Sep 27, 2026
Merged

alexander-sei merged 3 commits into
mainfrom
fix/link-check-github-false-positives

Conversation

@alexander-sei

@alexander-sei alexander-sei commented Sep 27, 2026 •

Copy link
Copy Markdown
Collaborator

What is the purpose of the change?

Fixes the external link check behind #81. None of the links it flagged are dead: all five URLs return 200. As in #77, GitHub answered the weekly lychee run with 503s, and the throttle added for #77 didn't stop them. One of the links had drifted, so this also corrects that page.

Describe the changes to the documentation

  • evm/wallet-integrations/particle.mdx: Particle renamed connectkit-aa-usage to connectkit-aa-demo, so the starter-repository link only worked through a redirect. It keeps the same pinned commit, and #L117 still lands on executeTxNative.
  • lychee.toml: GitHub file links (github.com/<owner>/<repo>/blob/<ref>/<path>) are now checked against raw.githubusercontent.com. All 8 GitHub false positives across 🔗 Broken external links detected #77 and 🔗 Broken external links detected #81 were rendered blob pages. The raw CDN still returns 404 for a missing file, ref, or repo, so real rot is still caught. A failing file link is reported with its raw URL, alongside the usual source file and line.
  • .github/workflows/external-links.yml: if the first pass fails, the job waits two minutes and re-checks before opening the tracking issue. With --cache, the second pass replays passing links from .lycheecache and re-requests only the failures, because lychee never caches a failure. The report now goes to the runner's temp directory, so the re-check's '**/*.md' glob doesn't read the first pass's report as a docs page.

Notes

  • Why the 🔗 Broken external links detected #77 throttle wasn't enough: lychee 0.24.2 retries a 429 but never a rejected 503. Slowing to one request per second also didn't reduce the 503s: 3 GitHub failures on Sep 14 before the throttle, 5 on Sep 21 with it.
  • Why not just accept 503: learnweb3.io answered 502/503 for weeks (🔗 Broken external links detected #52, 🔗 Broken external links detected #70, 🔗 Broken external links detected #73) and turned out to be dead (removed in 50054f0). Accepting 503 would have hidden it. With the re-check, anything that fails twice is still reported.
  • lychee version: already the latest stable. lycheeverse/lychee-action@v2 resolves to v2.9.0, which installs lychee v0.24.2, and lychee hasn't shipped a release since. The only newer build is nightly, which this PR doesn't switch to.
  • Verification: two manual runs of the external link check on this branch passed with 0 errors: 36308467046 and 36308932638, the second on the latest commit. Locally, with lychee 0.24.2 across the 142 tracked docs, the first pass took 46 s (57 s before the remap), and the second pass was served entirely from the cache. Against a mock server, a one-off 503 cleared on the re-check, while a persistent 503 and a 404 were still reported. actionlint, typos, and Vale are clean.
  • llms-full.txt keeps the old Particle URL until the weekly regeneration picks up the change.

Closes #81

alexander-sei and others added 2 commits September 27, 2026 11:08
Particle renamed connectkit-aa-usage to connectkit-aa-demo, so the
starter-repository link only resolved through a GitHub redirect. It
keeps the same pinned commit, and #L117 still lands on executeTxNative.

Co-authored-by: Cursor <cursoragent@cursor.com>
The five URLs flagged in #81 all return 200. As in #77, GitHub answered
the weekly check with 503s, and the per-host throttle added for #77 did
not stop them: 3 GitHub failures on Sep 14 without it, 5 on Sep 21 with
it. lychee 0.24.2 retries a rejected 429 but never a 503, so a single
503 becomes a reported error.

Every GitHub false positive so far was a rendered blob page, so remap
github.com/<owner>/<repo>/blob/<ref>/<path> links to
raw.githubusercontent.com, which still answers 404 for a missing file,
ref, or repo.

For every host, give failures a second look before filing an issue: if
the first pass fails, wait two minutes and run lychee again. --cache
lets the second pass replay passing links from .lycheecache and
re-request only the failures, since lychee never caches a failure.
Accepting 503 outright would have hidden learnweb3.io, which answered
502/503 for weeks (#52, #70, #73) before it was removed as dead.

Verified with lychee 0.24.2 across the 142 tracked docs: 0 errors, 46 s
for the first pass (57 s before the remap), and the second pass is
served entirely from the cache. Against a mock server, a one-off 503
cleared on the re-check while a persistent 503 and a 404 were still
reported.

Closes #81

Co-authored-by: Cursor <cursoragent@cursor.com>
@mintlify

mintlify Bot commented Sep 27, 2026 •

Copy link
Copy Markdown
Contributor

Preview deployment for your docs. Learn more about Mintlify Previews.

Project Status Preview Updated
sei-docs 🟢 Ready View Preview Sep 27, 2026, 9:09 AM

💡 Tip: Enable Automations to automatically generate PRs for you.

@cursor

cursor Bot commented Sep 27, 2026 •

Copy link
Copy Markdown

PR Summary

Low Risk
Changes affect CI link-checking and one documentation URL; no runtime, auth, or data-handling impact.

Overview
Reduces false broken-link issues from the weekly lychee job while still reporting URLs that are actually dead.

lychee.toml now remaps github.com/.../blob/... links to raw.githubusercontent.com so checks hit the CDN instead of rendered blob pages that often return transient 503s. Comments tie this to the existing GitHub throttle and the new workflow re-check.

.github/workflows/external-links.yml centralizes lychee args with --cache, writes the markdown report under runner.temp (so it is not scanned as docs), and on first-pass failure waits two minutes and runs a second pass that only re-requests failures. The tracking issue opens only when both passes fail.

evm/wallet-integrations/particle.mdx updates the Particle starter link from connectkit-aa-usage to connectkit-aa-demo (same commit and line anchor).

Reviewed by Cursor Bugbot for commit 6bc51b5. Bugbot is set up for automated code reviews on this repo. Configure here.

@seidroid seidroid Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This PR fixes the drifted Particle link, remaps GitHub blob links to raw.githubusercontent.com, and adds a cached second lychee pass before any issue is filed. The change is sound and I found no blockers. The main note is that the re-check pass also scans the report written by the first pass, because that report sits inside the glob.

Findings: 0 blocking | 7 non-blocking | 2 posted inline

Blockers

  • None at the file/PR level.

Non-blocking

  • REVIEW_GUIDELINES.md is empty, so this review applies no repo-specific guidelines beyond AGENTS.md.
  • The Cursor second-opinion pass (cursor-review.md) produced no output. Codex reported no material issues.
  • The PR relies on lychee not caching failures, which is what lets the second pass re-request only the links that failed. I couldn't check this against the lychee 0.24.2 source here. The author says a mock-server test confirmed it. To make it explicit, consider setting cache_exclude_status for 5xx codes in lychee.toml, so a cached 503 can never be replayed as a failure.
  • Remapped links now go to raw.githubusercontent.com, which is outside the [hosts."github.com"] throttle. That is fine given the CDN's limits. Blob links that point at directories, or that rely on repo-rename redirects, will behave differently, as the new comment says. That is acceptable, but worth knowing if new false 404s show up.
  • Once mint dev regenerates, llms-full.txt will pick up the new Particle URL through the scheduled workflow. No hand edit is needed, which matches AGENTS.md.
  • 2 suggestion(s)/nit(s) flagged inline on specific lines.

Comment thread .github/workflows/external-links.yml Outdated
args: ${{ env.LYCHEE_ARGS }}
fail: false
format: markdown
output: ./lychee-report.md

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[suggestion] The first pass writes ./lychee-report.md into the workspace. The second pass's '**/*.md' glob will then pick that report up as an input, because it isn't gitignored. Any URL that still fails will be checked again from the report and listed twice in the tracking issue, once from its real source and once from lychee-report.md. To avoid this, write the reports outside the checkout (for example ${{ runner.temp }}/lychee-report.md, and update content-filepath to match). Alternatively, add lychee-report.md to exclude_path.

output: ./lychee-report.md

- name: Open or update tracking issue on broken links
if: steps.lychee.outputs.exit_code != 0 && steps.recheck.outputs.exit_code != 0

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[nit] If the re-check step fails before it sets its output (for example, the action errors out), steps.recheck.outputs.exit_code is ''. In an expression, '' != 0 evaluates to false, so no issue is filed and the failure goes unnoticed. Consider also checking steps.recheck.outcome == 'failure', or using steps.recheck.outputs.exit_code != '0', so the step fails closed.

The first pass wrote ./lychee-report.md into the checkout, where the
re-check's '**/*.md' glob picks it up as a docs page, so every link
that still failed would be listed twice in the tracking issue. Write
the report to the runner's temp directory instead.

Co-authored-by: Cursor <cursoragent@cursor.com>

@seidroid seidroid Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This PR looks correct and well reasoned. The lychee remap sends blob links to raw.githubusercontent.com, the workflow now re-checks failures before filing an issue, and the Particle link is updated to the renamed repo. There are no blockers, only a few small robustness and maintainability notes.

Findings: 0 blocking | 5 non-blocking | 2 posted inline

Blockers

  • None at the file/PR level.

Non-blocking

  • The Cursor review file (cursor-review.md) was empty, so that pass produced no output. Codex reported no material issues, and I agree there is nothing blocking.
  • The repository's REVIEW_GUIDELINES.md is empty, so this review applies only general standards plus AGENTS.md.
  • In lychee, a remap runs before the exclude check. Any future exclude pattern aimed at github.com/.../blob/... would therefore never match; it would have to target raw.githubusercontent.com. Consider adding a one-line note next to the remap. No such exclude exists today, and I found no blob links that point at directories.
  • 2 suggestion(s)/nit(s) flagged inline on specific lines.

- name: Open or update tracking issue on broken links
if: steps.lychee.outputs.exit_code != 0 && steps.recheck.outputs.exit_code != 0
uses: peter-evans/create-issue-from-file@v5
with:

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[nit] If the re-check step itself errors (the action fails to install or crashes) and never sets exit_code, steps.recheck.outputs.exit_code is ''. '' != 0 evaluates true, so this step files an issue from whatever report is on disk, possibly the first-pass report. That's arguably the safe default. If you'd rather make it explicit, gate on steps.recheck.outcome == 'success' && steps.recheck.outputs.exit_code != 0, or let a failed step fail the job.

Comment thread lychee.toml
# 503s for files that exist. Reports show the raw URL. A blob link must point
# at a file; the CDN returns 404 for directories, so use /tree/ for those.
remap = [
"^https://github\\.com/([^/]+)/([^/]+)/blob/([^?#]+).* https://raw.githubusercontent.com/$1/$2/$3",

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[nit] The remap looks right: [^?#]+ drops ?plain=1 and #L117 anchors. One trade-off: a raw URL returns 200 for a renamed repo that only resolves through a redirect, which is exactly the drift fixed in particle.mdx here. Stale-but-redirecting blob links will stop surfacing. That's acceptable for a rot check, but worth knowing.

@alexander-sei
alexander-sei merged commit 600b2fa into main Sep 27, 2026
18 of 19 checks passed
@alexander-sei
alexander-sei deleted the fix/link-check-github-false-positives branch September 27, 2026 09:25
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

🔗 Broken external links detected

1 participant