Skip to content

fix(errors): point error docs links at a page that exists - #3589

Merged
kojiwakayama merged 4 commits into
mainfrom
fix/error-docs-dead-links
Aug 11, 2026
Merged

fix(errors): point error docs links at a page that exists#3589
kojiwakayama merged 4 commits into
mainfrom
fix/error-docs-dead-links

Conversation

@kojiwakayama

@kojiwakayama kojiwakayama commented Aug 11, 2026

Copy link
Copy Markdown
Contributor

The defect

Every boundary that reports a VeryfrontError prints a documentation URL built from the error's slug — the CLI formatter, the RFC 9457 type field, and the log line all go through buildErrorDocsUrl. It built https://veryfront.com/docs/errors/<slug>, and no /docs/errors/** section has ever existed, on the site or in veryfront-docs.

Reproduced before changing anything:

$ curl -s -o /dev/null -w "%{http_code}" https://veryfront.com/docs/errors/unknown-error
404
$ curl -s -o /dev/null -w "%{http_code}" https://veryfront.com/docs/errors/template-not-found
404
$ curl -s -o /dev/null -w "%{http_code}" https://veryfront.com/docs/errors
404

All 107 registered slugs are affected, so every error a user has ever seen ends with a dead link.

Why anchors on one page, not a page per slug

The brief suggested registry entries might carry title/description/suggestion/category — enough for a page each. They don't. ErrorDefinition (src/errors/types.ts) is:

slug, category, status, title, suggestion?, exitCode?

There is no description field. Reading actual entries, title is a short noun phrase and suggestion is one imperative line:

export const PORT_IN_USE = defineError({
  slug: "port-in-use",
  category: "SERVER",
  status: 409,
  title: "Server port already in use",
  suggestion: "Use a different port or stop the process using this port",
});

107 pages of two short lines each is precisely the "wall of near-empty stubs" the brief warned is worse than one good page. So slugs become anchors on one generated reference page: docs/guides/errors.md, grouped by category, published at veryfront.com/docs/code/guides/errors through the existing sync-docs path. ERROR_DOCS_BASE_URL becomes that page plus #; every caller follows with no further change.

This keeps option (a)'s real benefit — the destination is generated from the registry, so it cannot drift — without the stub problem. If entries ever grow real prose, splitting into pages is a generator change, not a re-plumbing of every boundary.

Changes

  • scripts/docs/generate-error-reference.ts generates the page from the registry, following the generate-api-reference.ts precedent, with a --check staleness mode wired into lint:ci, verify, and verify:quick. It fails loudly on an error category with no page section rather than silently dropping its errors.
  • ERROR_DOCS_BASE_URL now points at the real page.
  • src/errors/user-friendly/error-catalog.ts had hardcoded one of these URLs instead of building it; it now calls buildErrorDocsUrl.

The regression test

tests/docs/error-docs-links.test.ts iterates the registry rather than sampling slugs. It asserts:

  1. every slug's built URL resolves to the published page's path and to an anchor that exists on it;
  2. the unknown-error fallback used for unusable slugs resolves too;
  3. the page carries no anchors for slugs the registry no longer emits;
  4. no shipped source hardcodes an error docs link instead of calling buildErrorDocsUrl.

It derives the local file from ERROR_DOCS_BASE_URL, so moving the page without moving the constant fails.

Confirmed failing before the fix for the right reason, and confirmed to catch a new dead link by adding an unpublished slug to the registry:

probe-dead-link: no "#probe-dead-link" anchor in docs/guides/errors.md
probe-dead-link: title
probe-dead-link: suggestion

The --check gate catches the same mistake as docs/guides/errors.md is stale.

Security-test shape change

safe-diagnostics.test.ts and types.test.ts pinned a hostile slug to a path segment. The slug is now a fragment, so those tests assert the same property in its new shape: a hostile slug cannot change the page the link points at, open a query, or start a second fragment. encodeURIComponent still applies, and confinement to a fragment is if anything stronger than confinement to a path segment.

Verification

deno task lint, deno task typecheck, deno task docs:errors:check, scripts/lint/check-doc-links.ts (1230 links OK), and the full pre-push gate all pass.

One pre-existing, unrelated docs:validate failure is present on origin/main and is not touched here: docs/guides/deploying.md:63 uses an em dash, from #3566. docs:validate is not part of the CI lint gate.

Follow-up

The generated page also needs to reach veryfront-docs for the links to resolve in production. The sync-docs workflow already covers docs/guides/**, so this should propagate on merge to main — worth confirming the dispatch lands before announcing the links as live.

Summary by CodeRabbit

  • New Features

    • Added a comprehensive error-reference guide covering error meanings, status codes, and recommended actions.
    • Added a Troubleshoot section linking to the error reference.
    • Error messages now link directly to the relevant section of the guide.
  • Bug Fixes

    • Improved error-documentation links, including safer handling of special characters in error identifiers.
  • Documentation

    • Added automatic generation and validation of the error reference to keep it synchronized with available errors.

@coderabbitai

coderabbitai Bot commented Aug 11, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@kojiwakayama, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 16 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: dbd50ea0-d808-4a3c-8df8-c2e381fe724f

📥 Commits

Reviewing files that changed from the base of the PR and between f606bbe and cdb3ceb.

📒 Files selected for processing (6)
  • docs/api-reference/veryfront/errors.md
  • docs/guides/errors.md
  • scripts/docs/generate-error-reference.ts
  • src/errors/README.md
  • src/errors/catalog/factory.test.ts
  • tests/docs/error-docs-links.test.ts
📝 Walkthrough

Walkthrough

The PR adds generated error-reference documentation and CI validation. It changes error URLs from individual paths to fragment anchors on the guide page, updates runtime references and tests, and validates registry-to-documentation coverage.

Changes

Error documentation and URL migration

Layer / File(s) Summary
Reference generation and CI wiring
scripts/docs/generate-error-reference.ts, docs/guides/errors.md, docs/guides/index.md, deno.json
Generates the error guide from registry metadata and runs freshness checks in verification tasks.
Error URL contract and runtime usage
src/errors/diagnostic-policy.ts, src/errors/http-error.ts, src/errors/user-friendly/error-catalog.ts, src/errors/README.md
Uses the generated guide URL with error slugs as fragments.
URL migration and encoding tests
src/errors/**, src/server/**, tests/e2e/**
Updates expected URLs and checks fragment encoding, pathname preservation, and boundary cases.
Documentation coverage validation
tests/docs/error-docs-links.test.ts, tests/docs/guide-contracts.test.ts
Checks registry anchors, metadata, stale slugs, shared URL construction, and guide contracts.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Sequence Diagram(s)

sequenceDiagram
  participant ErrorRegistry
  participant Generator
  participant ErrorGuide
  participant CI
  ErrorRegistry->>Generator: provide registered errors
  Generator->>ErrorGuide: generate and format errors.md
  CI->>Generator: run docs:errors:check
  Generator-->>CI: report documentation status
Loading

Possibly related PRs

Suggested reviewers: kwakayama, ariskemper

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 75.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the primary change: redirecting error documentation links to an existing page.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches 💡 2
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🛠️ Fix failing CI checks 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/error-docs-dead-links

Comment @coderabbitai help to get the list of available commands.

@kojiwakayama
kojiwakayama force-pushed the fix/error-docs-dead-links branch from f606bbe to 8567b15 Compare August 11, 2026 13:12

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 4

🧹 Nitpick comments (2)
scripts/docs/generate-error-reference.ts (1)

16-18: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Use the internal import map for cross-module imports.

Replace the relative src/errors imports with #veryfront/errors/* imports.

  • scripts/docs/generate-error-reference.ts#L16-L18: import the registry, diagnostic policy, and types through #veryfront/errors/*.
  • tests/docs/error-docs-links.test.ts#L3-L7: import the registry and diagnostic policy through #veryfront/errors/*.

Based on learnings: “Do not add relative internal imports outside the cli/ directory.”

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@scripts/docs/generate-error-reference.ts` around lines 16 - 18, Replace the
relative imports in scripts/docs/generate-error-reference.ts lines 16-18 with
the corresponding `#veryfront/errors/`* imports for the registry, diagnostic
policy, and ErrorCategory type. Also update tests/docs/error-docs-links.test.ts
lines 3-7 to import the registry and diagnostic policy through
`#veryfront/errors/`*; both sites require direct changes.

Source: Learnings

src/errors/diagnostic-policy.ts (1)

15-24: 🗄️ Data Integrity & Integration | 🔵 Trivial

Confirm documentation publication before release.

ERROR_DOCS_BASE_URL now points to the generated guide. Synchronize and deploy that guide to veryfront-docs before this runtime change reaches production. Otherwise, emitted Docs: links remain dead.

Add this synchronization to the release or deployment gate.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/errors/diagnostic-policy.ts` around lines 15 - 24, Update the release or
deployment gate that publishes documentation to synchronize and deploy the
generated errors guide to veryfront-docs before the runtime change is released.
Ensure the generated guide is available at ERROR_DOCS_BASE_URL so emitted Docs
links resolve to its anchors.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@scripts/docs/generate-error-reference.ts`:
- Around line 160-167: Update formatMarkdown to capture the result of
command.output(), check its success status, and throw when deno fmt exits
unsuccessfully before the caller reports completion. Preserve the existing
formatting command and arguments.

In `@src/errors/catalog/factory.test.ts`:
- Around line 438-443: Update the hydration URL fixture and matching assertion
in the relevant error catalog test to use the single valid anchor from
docs/guides/errors.md, removing the second fragment delimiter while keeping both
values identical.

In `@src/errors/README.md`:
- Line 73: Update the slug-encoding paragraph in the errors README to describe
<slug> as a URL fragment rather than a documentation URL path segment, while
preserving the existing encoding and redaction guarantees.

In `@src/errors/user-friendly/error-catalog.ts`:
- Line 2: Update the cross-module import in error-catalog.ts to use the
configured `#veryfront/`* internal alias for diagnostic-policy.ts instead of a
relative path, while preserving the existing buildErrorDocsUrl import.

---

Nitpick comments:
In `@scripts/docs/generate-error-reference.ts`:
- Around line 16-18: Replace the relative imports in
scripts/docs/generate-error-reference.ts lines 16-18 with the corresponding
`#veryfront/errors/`* imports for the registry, diagnostic policy, and
ErrorCategory type. Also update tests/docs/error-docs-links.test.ts lines 3-7 to
import the registry and diagnostic policy through `#veryfront/errors/`*; both
sites require direct changes.

In `@src/errors/diagnostic-policy.ts`:
- Around line 15-24: Update the release or deployment gate that publishes
documentation to synchronize and deploy the generated errors guide to
veryfront-docs before the runtime change is released. Ensure the generated guide
is available at ERROR_DOCS_BASE_URL so emitted Docs links resolve to its
anchors.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 635534b4-6dc1-49aa-be88-4b4bfc76f5e0

📥 Commits

Reviewing files that changed from the base of the PR and between 46fe6da and f606bbe.

⛔ Files ignored due to path filters (1)
  • src/server/services/rsc/endpoints/rsc-bundles.generated.ts is excluded by !**/*.generated.*
📒 Files selected for processing (28)
  • deno.json
  • docs/guides/errors.md
  • docs/guides/index.md
  • scripts/docs/generate-error-reference.ts
  • src/errors/README.md
  • src/errors/catalog/config-errors.test.ts
  • src/errors/catalog/factory.test.ts
  • src/errors/diagnostic-policy.ts
  • src/errors/error-registry.test.ts
  • src/errors/http-error.test.ts
  • src/errors/http-error.ts
  • src/errors/logging.test.ts
  • src/errors/middleware/cli-error-boundary.test.ts
  • src/errors/middleware/cli-error-boundary.ts
  • src/errors/middleware/http-error-boundary.test.ts
  • src/errors/safe-diagnostics.test.ts
  • src/errors/types.test.ts
  • src/errors/user-friendly/error-catalog.test.ts
  • src/errors/user-friendly/error-catalog.ts
  • src/server/handlers/request/agent-stream.handler.test.ts
  • src/server/handlers/request/api/api-handler-wrapper.test.ts
  • src/server/handlers/request/module/module.handler.test.ts
  • src/server/handlers/request/ssr/ssr.handler.test.ts
  • src/server/runtime-handler/timeout-manager.test.ts
  • src/server/services/rsc/endpoints/endpoint-router.test.ts
  • tests/docs/error-docs-links.test.ts
  • tests/docs/guide-contracts.test.ts
  • tests/e2e/regressions/rsc-proxy-hydration.test.ts

Comment thread scripts/docs/generate-error-reference.ts
Comment thread src/errors/catalog/factory.test.ts Outdated
Comment thread src/errors/README.md
Comment thread src/errors/user-friendly/error-catalog.ts
@kojiwakayama
kojiwakayama force-pushed the fix/error-docs-dead-links branch 2 times, most recently from 6531afe to 0308a5a Compare August 11, 2026 13:32
Every boundary that reports a VeryfrontError prints a documentation URL built
from the error's slug: the CLI formatter, the RFC 9457 `type` field, and the
log line all call `buildErrorDocsUrl`. That built
`https://veryfront.com/docs/errors/<slug>`, and no `/docs/errors/**` section
has ever existed - not on the site, not in veryfront-docs. All 107 registered
slugs 404, so every error a user has ever seen ends with a dead link.

Registry entries do not carry enough for a page each. `ErrorDefinition`
(src/errors/types.ts) is slug, category, status, title, suggestion, exitCode -
there is no description field. A generated page per slug would be a title line
and a one-line suggestion, 107 times over. So the slugs become anchors on one
generated reference page instead: docs/guides/errors.md, grouped by category,
published at veryfront.com/docs/code/guides/errors via the existing sync-docs
path. ERROR_DOCS_BASE_URL becomes that page plus `#`, and every caller follows
without further change.

The page is generated from the registry by
scripts/docs/generate-error-reference.ts, following the
generate-api-reference.ts precedent, with a `--check` staleness mode wired into
lint:ci, verify, and verify:quick. Generation fails loudly on an error category
that has no page section rather than dropping its errors silently.

Regression test in tests/docs/error-docs-links.test.ts iterates the registry
rather than sampling slugs: every slug's built URL must resolve to the
published page's path and to an anchor that exists on it, the `unknown-error`
fallback for unusable slugs must resolve too, the page must carry no anchors
for slugs the registry no longer emits, and no shipped source may hardcode an
error docs link instead of calling buildErrorDocsUrl. Verified by adding an
unpublished slug to the registry: the test fails with `probe-dead-link: no
"#probe-dead-link" anchor in docs/guides/errors.md`.

src/errors/user-friendly/error-catalog.ts had hardcoded one of these URLs
rather than building it; it now calls buildErrorDocsUrl, which the new
hardcoded-link check keeps that way.

The safe-diagnostics and types tests that pinned a hostile slug to a path
segment now pin it to the fragment, asserting the same property in its new
shape: a hostile slug cannot change the page the link points at, open a query,
or start a second fragment.
The published pages render as MDX, and `mintlify broken-links` rejected the
generated page twice:

  Unable to parse code/guides/errors.md - 14:2: Unexpected character `!`
  Unable to parse code/guides/errors.md - 355:3: Expected a closing tag for
    `<number>` before the end of `paragraph`

The provenance HTML comment is not valid MDX, so it becomes a visible sentence
in the intro instead. And two registry suggestions carry CLI placeholders
(`veryfront dev --port <number>`, `veryfront push --branch <name>`) that MDX
reads as unclosed JSX tags, so all registry-derived text is now escaped for the
characters MDX treats as markup.

Escaping is applied generally rather than to those two strings: any future
error message containing a placeholder would otherwise break the docs build.
A new case in tests/docs/error-docs-links.test.ts asserts the page carries no
unescaped MDX markup characters at all, which is the check that would have
caught this without a Mintlify run.

Generation now runs only under `import.meta.main` so the test can import
escapeMdxText without rewriting the committed page.

Verified with `npx mintlify@latest broken-links` on node 22: no broken links.
CI's `ci (lint)` failed on `docs/api-reference is stale: outdated:
veryfront/errors.md`. The new import in src/errors/user-friendly/error-catalog.ts
shifted two source line pins by one, and those pins are baked into the
generated reference.

Regenerated with `deno task docs` under the Deno version CI pins
(2.7.7, per .github/actions/setup-deno/action.yml) rather than the local
default, which reports every page as outdated. The diff is two line pins and
nothing else.
Three findings from the automated review, all valid:

- The blanket URL rewrite turned a fixture's `.../docs/errors/hydration-mismatch#hydration`
  into `.../errors#hydration-mismatch#hydration`. A second `#` is not a second
  anchor -- the browser reads the whole tail as one fragment. The fixture now
  uses the single anchor that exists on the page.
- generate-error-reference.ts ignored a failing `deno fmt`, so a formatter
  failure would write an unformatted page and, in --check mode, compare against
  an unformatted expectation -- reporting "current" for a page that fails
  `deno fmt --check`. It now throws with the formatter's stderr. Verified by
  stubbing `deno fmt` to exit 1: the generator fails instead of reporting
  success.
- src/errors/README.md still described the slug as a URL path segment. It is a
  fragment now, and the guarantee it documents is correspondingly stronger: a
  slug can never change the page the link points at.

Not applied: the suggestion to import diagnostic-policy.ts via
`#veryfront/errors/diagnostic-policy.ts`. No such subpath exists in the import
map (only `#veryfront/errors` -> index.ts), so that specifier would not
resolve, and every other file in src/errors/user-friendly/ already imports its
siblings relatively (../catalog/types.ts, ../safe-diagnostics.ts, ../types.ts).
@kojiwakayama
kojiwakayama force-pushed the fix/error-docs-dead-links branch from 0308a5a to cdb3ceb Compare August 11, 2026 13:42
@kojiwakayama
kojiwakayama added this pull request to the merge queue Aug 11, 2026
@github-merge-queue
github-merge-queue Bot removed this pull request from the merge queue due to failed status checks Aug 11, 2026
@kojiwakayama
kojiwakayama added this pull request to the merge queue Aug 11, 2026
@github-merge-queue
github-merge-queue Bot removed this pull request from the merge queue due to failed status checks Aug 11, 2026
@kojiwakayama
kojiwakayama added this pull request to the merge queue Aug 11, 2026
@github-merge-queue
github-merge-queue Bot removed this pull request from the merge queue due to failed status checks Aug 11, 2026
@kojiwakayama
kojiwakayama added this pull request to the merge queue Aug 11, 2026
Merged via the queue into main with commit 07683f1 Aug 11, 2026
35 checks passed
@kojiwakayama
kojiwakayama deleted the fix/error-docs-dead-links branch August 11, 2026 16:26
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.

1 participant