.claude/skills/solidjs/solid-errors/solid-errors-error-handling/SKILL.md
Use when implementing error boundaries, Suspense loading states, or error recovery in SolidJS components. Prevents uncaught rendering errors, missing fallback UIs, and incorrect ErrorBoundary/Suspense nesting. Covers ErrorBoundary with reset, Suspense for createResource, nested boundaries, caught vs uncaught errors, and combined error/loading patterns. Keywords: ErrorBoundary, Suspense, error handling, fallback, reset, createResource, loading state, error recovery.
npx skillsauth add OpenAEC-Foundation/OpenAEC-Workspace-Composer solid-errors-error-handlingInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
| Aspect | Details |
|--------|---------|
| Import | import { ErrorBoundary } from "solid-js"; |
| Purpose | Catches errors in child reactive computations and rendering |
| Fallback | Static JSX or function receiving (err, reset) |
| Reset | Calls reset() to clear error state and re-render children |
| Version | Available since SolidJS 1.0 |
| Aspect | Details |
|--------|---------|
| Import | import { Suspense } from "solid-js"; |
| Purpose | Shows fallback while createResource calls resolve |
| Behavior | Creates DOM immediately but delays attachment until resources resolve |
| Nested | Each boundary resolves independently |
| Version | Available since SolidJS 1.0 |
| Catches | Does NOT Catch |
|---------|----------------|
| Errors during JSX rendering | Errors in event handlers (onClick, etc.) |
| Errors in createEffect | Errors in setTimeout / setInterval callbacks |
| Errors in createMemo | Errors in async code outside reactive scope |
| Errors in createResource | Errors in the ErrorBoundary's own fallback |
| Errors in reactive computations | Errors thrown at module/top-level scope |
NEVER rely on try/catch for rendering errors -- SolidJS rendering is compiled to direct DOM operations. A try/catch in a component body runs once during setup and CANNOT catch errors that occur later in reactive updates.
NEVER throw errors inside an ErrorBoundary fallback function expecting the SAME boundary to catch them -- errors in the fallback propagate to the PARENT boundary. If no parent boundary exists, the error is uncaught.
ALWAYS wrap components using createResource in BOTH <Suspense> AND <ErrorBoundary> -- Suspense handles loading states while ErrorBoundary handles fetch failures.
ALWAYS place <ErrorBoundary> OUTSIDE <Suspense> when protecting against resource errors -- if ErrorBoundary is inside Suspense, a resource error may not display the error fallback correctly.
NEVER assume createEffect errors are caught during SSR -- createEffect NEVER runs during SSR. Use isServer checks for server-side error handling.
import { ErrorBoundary } from "solid-js";
<ErrorBoundary fallback={<p>Something went wrong.</p>}>
<App />
</ErrorBoundary>
import { ErrorBoundary } from "solid-js";
<ErrorBoundary
fallback={(err: Error, reset: () => void) => (
<div class="error-panel">
<h2>Error occurred</h2>
<p>{err.message}</p>
<button onClick={reset}>Try Again</button>
</div>
)}
>
<DataView />
</ErrorBoundary>
When reset() is called, ErrorBoundary clears its error state and re-renders its children from scratch.
Inner boundaries catch first. If the inner boundary's fallback itself throws, the error propagates to the outer boundary:
<ErrorBoundary fallback={<p>App-level error</p>}>
<Header />
<ErrorBoundary
fallback={(err, reset) => (
<div>
<p>Section error: {err.message}</p>
<button onClick={reset}>Retry</button>
</div>
)}
>
<DataSection />
</ErrorBoundary>
<Footer />
</ErrorBoundary>
If <DataSection /> throws, only the inner boundary activates. <Header /> and <Footer /> remain visible.
import { Suspense, createResource } from "solid-js";
const [data] = createResource(fetchData);
<Suspense fallback={<LoadingSpinner />}>
<DataDisplay data={data()} />
</Suspense>
| Aspect | <Suspense> | <Show when={resource()}> |
|--------|-------------|---------------------------|
| DOM creation | Creates children immediately, delays attachment | Destroys and recreates children on toggle |
| State preservation | Preserves internal state across loading cycles | Loses all internal state on each toggle |
| Resource tracking | Automatically tracks createResource calls | Manual condition checking |
| Use case | Async data loading | Conditional rendering |
ALWAYS use <Suspense> for createResource loading states. NEVER use <Show> as a loading gate for resources -- it destroys and recreates the entire subtree, losing state.
<Suspense fallback={<HeaderSkeleton />}>
<Header user={userResource()} />
<Suspense fallback={<ContentSkeleton />}>
<Content data={dataResource()} />
<Suspense fallback={<CommentsSkeleton />}>
<Comments comments={commentsResource()} />
</Suspense>
</Suspense>
</Suspense>
Each boundary resolves independently -- the header shows as soon as userResource resolves, without waiting for content or comments.
onMount inside Suspense runs AFTER resources resolvecreateEffect inside Suspense runs AFTER resources resolveimport { ErrorBoundary, Suspense, createResource } from "solid-js";
const [data] = createResource(fetchData);
<ErrorBoundary
fallback={(err, reset) => (
<div>
<p>Failed to load: {err.message}</p>
<button onClick={reset}>Retry</button>
</div>
)}
>
<Suspense fallback={<LoadingSpinner />}>
<DataDisplay data={data()} />
</Suspense>
</ErrorBoundary>
Why this order: When a resource rejects, the error propagates upward. ErrorBoundary outside Suspense catches it and displays the error fallback. If ErrorBoundary were inside Suspense, the Suspense boundary would not know to stop showing its fallback.
import { ErrorBoundary, Suspense, createResource } from "solid-js";
function DataPanel() {
const [data, { refetch }] = createResource(fetchData);
return (
<ErrorBoundary
fallback={(err, reset) => (
<div>
<p>Error: {err.message}</p>
<button onClick={() => { reset(); refetch(); }}>
Retry
</button>
</div>
)}
>
<Suspense fallback={<p>Loading...</p>}>
<div>{data()?.title}</div>
</Suspense>
</ErrorBoundary>
);
}
ALWAYS call both reset() AND refetch() when recovering from resource errors. reset() clears the error state; refetch() re-triggers the data fetch.
import { ErrorBoundary, createSignal } from "solid-js";
function RecoverableForm() {
const [formData, setFormData] = createSignal({ name: "", email: "" });
return (
<ErrorBoundary
fallback={(err, reset) => (
<div>
<p>Form error: {err.message}</p>
<button onClick={() => {
setFormData({ name: "", email: "" });
reset();
}}>
Reset Form
</button>
</div>
)}
>
<FormContent data={formData()} onUpdate={setFormData} />
</ErrorBoundary>
);
}
import { catchError, createEffect } from "solid-js";
function MonitoredComponent() {
catchError(
() => {
createEffect(() => {
// Reactive computation that might fail
riskyOperation();
});
},
(err) => {
console.error("Caught in reactive scope:", err);
reportToErrorService(err);
}
);
}
catchError provides programmatic error handling within reactive scopes without rendering a fallback UI. Use it for logging, reporting, or silent recovery.
createEffect NEVER runs during SSR -- errors in effects are client-onlySuspense on the server enables streaming HTML -- resources resolve server-sideisServer from solid-js/web when error handling logic differs between server and clientIs the error from a reactive computation or rendering?
├── YES → Use <ErrorBoundary>
│ ├── Need error details + retry? → fallback={(err, reset) => ...}
│ ├── Simple error display? → fallback={<ErrorMessage />}
│ └── Need programmatic handling too? → Add catchError inside
└── NO (event handler, setTimeout, etc.)
└── Use standard try/catch in the handler
Is data from createResource?
├── YES → Use <Suspense>
│ ├── Multiple independent resources? → Nested <Suspense> boundaries
│ ├── Resource might fail? → Wrap with <ErrorBoundary> OUTSIDE
│ └── Need previous data during refresh? → Use resource.latest
└── NO (signal-based state)
└── Use <Show when={data()}> for conditional rendering
development
Use when integrating Vite with a backend framework, rendering Vite assets from server-side templates, or setting up dev/production HTML serving. Prevents incorrect manifest.json traversal and missing CSS chunk resolution in production. Covers build.manifest configuration, .vite/manifest.json structure, ManifestChunk properties, dev mode HTML setup, production rendering, CSS/JS chunk resolution, and modulepreload polyfill. Keywords: backend integration, manifest.json, ManifestChunk, Django, Laravel, Rails, modulepreload.
development
Use when encountering dev server startup failures, HMR issues, proxy errors, CORS blocks, or module not found errors during development. Prevents misconfiguring server.hmr behind reverse proxies and forgetting appType: 'custom' in middleware mode. Covers HMR full-reload debugging, proxy configuration, CORS setup, HTTPS certificates, server.fs.strict violations, port conflicts, WebSocket failures, file watcher issues, and middleware mode. Keywords: dev server, HMR, proxy, CORS, HTTPS, WebSocket, port conflict, server.fs.strict, middleware mode, file watcher.
development
Use when encountering pre-bundling errors, dependency resolution failures, stale cache issues, or slow development server startup. Prevents excluding CJS dependencies from pre-bundling (which breaks runtime module resolution) and misconfiguring optimizeDeps. Covers CJS/ESM conversion failures, missing dependency auto-discovery, optimizeDeps configuration, monorepo linked dependencies, cache invalidation, browser cache staleness, and large dependency tree performance. Keywords: pre-bundling, optimizeDeps, CJS, ESM, cache, dependency resolution, monorepo, node_modules/.vite.
development
Use when encountering Vite build failures, chunk size warnings, or version-specific build errors. Prevents the common mistake of using deprecated rollupOptions in v8 or misconfiguring build targets and minifiers. Covers Rolldown/Rollup bundling failures, CSS minification errors, sourcemap problems, library mode build failures, BundleError handling, and asset processing errors. Keywords: build error, Rolldown, chunk size, sourcemap, library mode, minify, BundleError, rollupOptions, build.target.