← 返回 Skills 市场
anderskev

Remix V2 Error Boundaries Review

作者 Kevin Anderson · GitHub ↗ · v1.0.0 · MIT-0
cross-platform ⚠ pending
50
总下载
0
收藏
0
当前安装
1
版本数
在 OpenClaw 中安装
/install remix-v2-error-boundaries-review
功能描述
Reviews Remix v2 error-handling code for the unified ErrorBoundary, isRouteErrorResponse narrowing, throw-vs-return, root boundary scaffolding, and v1 holdov...
使用说明 (SKILL.md)

Remix v2 Error Boundaries Code Review

Targets TypeScript route modules importing from @remix-run/*. No sibling knowledge skill exists for this topic; the canonical mental model is summarized inline below and expanded in references/.

v2 Boundary Model (read first)

Remix v2 unified v1's CatchBoundary + ErrorBoundary into a single ErrorBoundary route-module export. The framework calls it for both thrown Responses (e.g. throw new Response(...), throw json(...)) and thrown runtime errors (loader/action/render exceptions). Inside the boundary you read the value with the useRouteError() hook, then narrow in this order:

  1. isRouteErrorResponse(error) → it was a thrown Response; read error.status, error.statusText, error.data.
  2. error instanceof Error → real runtime error; read error.message.
  3. else → unknown thrown value; render a generic fallback.

The boundary takes no props. CatchBoundary, useCatch, and the future.v2_errorBoundary flag are all gone — finding any of them is a v1 holdover. Errors render the nearest ErrorBoundary and bubble to the root if none exists; the root boundary remounts the whole document, so it must render \x3CMeta />, \x3CLinks />, and \x3CScripts />. Only thrown loader/action results reach the boundary — a return json(...) with a 4xx status is a successful loader, not an error. Server-side runtime errors also flow through an optional entry.server.tsx handleError export (thrown Responses do not).

Quick Reference

Issue Type Reference
Missing route ErrorBoundary, props-on-boundary, narrowing-only instanceof Error, narrowing-only isRouteErrorResponse references/boundary-shape.md
Return-instead-of-throw 4xx/5xx, swallowing error.data, throwing strings, missing handleError references/throw-response.md
Missing root boundary, root boundary without \x3CMeta />/\x3CLinks />/\x3CScripts />, useLoaderData() in root boundary references/root-boundary.md
CatchBoundary export, useCatch import, v2_errorBoundary future flag references/v1-holdovers.md

Review Checklist

  • ErrorBoundary declared export function ErrorBoundary() with no props
  • Error read via useRouteError(), not useCatch() and not a prop
  • Narrowing checks isRouteErrorResponse(error) first, then error instanceof Error, then fallback
  • error.data rendered defensively (typed/narrowed before going into JSX)
  • 4xx / 5xx in loaders/actions use throw (not return) for Response / json
  • Routes that can throw export their own ErrorBoundary (don't tear down parents for a widget failure)
  • Root app/root.tsx exports an ErrorBoundary that renders \x3CMeta />, \x3CLinks />, and \x3CScripts />
  • Root boundary uses useRouteLoaderData("root") (not useLoaderData()) when reading root data
  • No CatchBoundary export anywhere; no useCatch import; no future.v2_errorBoundary in remix.config.js
  • entry.server.tsx exports handleError and pipes runtime errors to an error reporter
  • handleError does not assume thrown Responses flow through it (they don't)
  • Thrown values are Response/json/Error instances — never plain strings or POJOs

Valid Patterns (Do NOT Flag)

These are correct Remix v2 usage and must not be reported as issues:

  • Route without ErrorBoundary that intentionally inherits from a parent — Boundaries cascade up. A child route may omit ErrorBoundary so the parent (or root) renders the fallback. Only flag if the route handles user-distinct error UX and a parent boundary cannot.
  • throw new Response(...) or throw json(...) from a loader/action — The canonical way to signal 404/401/403/etc. This is not "using exceptions for control flow"; it is documented v2 contract.
  • Narrowing only with isRouteErrorResponse(error) — Acceptable when the route demonstrably only throws Responses and has no render-time crash risk. Severity is ADVISORY at most; suggest adding an instanceof Error branch for defense-in-depth, do not flag as a bug.
  • ErrorBoundary that does not call useRouteError() — Valid when the boundary renders a static "Something went wrong" fallback intentionally (e.g. marketing pages that don't want to surface error detail).
  • Root ErrorBoundary calling useRouteLoaderData("root") and getting undefined — Documented defensive pattern (root loader may have thrown). Do not flag the undefined handling as "dead code."
  • handleError returning early on request.signal.aborted — Documented noise filter, not a swallowed error.
  • handleError not handling thrown Responses — By framework contract handleError only fires for runtime errors. The absence of Response handling is correct, not a gap.
  • Nested ErrorBoundary returning a bare fragment (no \x3Chtml> / \x3Cbody>) — Only the root boundary owns the document. Nested boundaries render inside parent layouts and must not include document tags.

Severity guidance

Use these defaults unless the codebase has documented a different scale:

Pattern Default severity
CatchBoundary export or useCatch import in v2 codebase BLOCKER (build-breaking or dead code)
Root ErrorBoundary missing \x3CScripts /> BLOCKER (dead-end error page)
ErrorBoundary with ({ error }) v1 prop signature WARN (silent runtime undefined)
return json(...) for 4xx instead of throw WARN (boundary never fires)
Missing instanceof Error branch on a route with render-crash risk WARN
Missing instanceof Error branch on a Response-only route ADVISORY
useLoaderData() (vs useRouteLoaderData) in root boundary WARN (latent loop)
Missing handleError in entry.server.tsx ADVISORY (observability gap, not a bug)

Hard gates (before writing findings)

Run in order. Do not draft user-facing findings until every gate passes for the batch you are about to report.

  1. Location evidencePass: Each issue lists the repo path to the route module (or app/root.tsx, or app/entry.server.tsx) and either a line range or a short verbatim quote from the file you read (not from memory or diff-only guesswork). "The root boundary is wrong" without a path to app/root.tsx is not reportable.

  2. Exemption checkPass: For each issue, you can state in one line why it is not covered by Valid Patterns (Do NOT Flag). In particular: confirm a missing ErrorBoundary is not a deliberate cascade to a parent boundary; confirm an isRouteErrorResponse-only narrowing is not on a route that demonstrably only throws Responses (downgrade to ADVISORY in that case).

  3. v1-vs-v2 marker checkPass: Before writing the finding, grep the route module (and the repo at large for cross-cutting issues) for: CatchBoundary, useCatch, v2_errorBoundary, ErrorBoundary({ error, ErrorBoundary({error. If any of these appear, the finding is a v1 holdover (load references/v1-holdovers.md) and must be labeled as such — not as a generic "missing error handling" issue. If none appear, the code is v2-shape and the finding is about v2 correctness.

  4. ProtocolPass: You completed the Pre-Report Verification Checklist in review-verification-protocol for this review.

Review Questions

  1. Does every route that can throw (loader, action, or render) have an ErrorBoundary at the right level — local where the recovery UI matters, parent/root where cascade is intentional?
  2. Does each ErrorBoundary call useRouteError() (not useCatch(), not props) and narrow isRouteErrorResponse first?
  3. Are 4xx / 5xx control flows using throw (not return) so the boundary actually fires?
  4. Does app/root.tsx export an ErrorBoundary with \x3CMeta />, \x3CLinks />, and \x3CScripts />, and use useRouteLoaderData("root") defensively?
  5. Are there any v1 markers left (CatchBoundary, useCatch, v2_errorBoundary, ({ error }) prop signature)?
  6. Is handleError present in entry.server.tsx for runtime-error observability, with the correct contract (no Response handling)?

Additional Documentation

如何使用
  1. 确保已安装 OpenClaw(本地或 Docker 部署)
  2. 在对话框中输入安装命令:/install remix-v2-error-boundaries-review
  3. 安装完成后,直接呼叫该 Skill 的名称或使用 /remix-v2-error-boundaries-review 触发
  4. 根据 Skill 的参数说明提供必要输入,即可获得结构化输出
版本历史
v1.0.0
Initial release covering code review for Remix v2 error boundaries. - Provides guidance for unified ErrorBoundary in Remix v2, including correct narrowing with isRouteErrorResponse and instanceof Error. - Flags v1 holdovers like CatchBoundary, useCatch, and legacy prop signatures. - Details review checklist, severity guidance, and mandatory pre-report gates. - Summarizes valid patterns that must not be flagged as issues. - Includes references and quick lookup tables for common mistakes and best practices. - Meant to assist reviewers of TypeScript Remix v2 route modules.
元数据
Slug remix-v2-error-boundaries-review
版本 1.0.0
许可证 MIT-0
累计安装 0
当前安装数 0
历史版本数 1
常见问题

Remix V2 Error Boundaries Review 是什么?

Reviews Remix v2 error-handling code for the unified ErrorBoundary, isRouteErrorResponse narrowing, throw-vs-return, root boundary scaffolding, and v1 holdov... 它是一个面向 Claude Code / OpenClaw 的 AI Agent Skill 插件,目前累计下载 50 次。

如何安装 Remix V2 Error Boundaries Review?

在 OpenClaw 或 Claude Code 对话框中运行命令「/install remix-v2-error-boundaries-review」即可一键安装,无需额外配置。

Remix V2 Error Boundaries Review 是免费的吗?

是的,Remix V2 Error Boundaries Review 完全免费,采用 MIT-0 许可证,可自由下载、安装和使用。

Remix V2 Error Boundaries Review 支持哪些平台?

Remix V2 Error Boundaries Review 跨平台运行,可在任意部署了 OpenClaw / Claude Code 的环境中使用(cross-platform)。

谁开发了 Remix V2 Error Boundaries Review?

由 Kevin Anderson(@anderskev)开发并维护,当前版本 v1.0.0。

💬 留言讨论