Error Handling

The plugin has a structured error system in plugin/error/. Here's how it works.

Panic Threshold

The central concept is panicThreshold — a config option that controls whether errors stop the build or get swallowed:

Value Behavior
"none" Log errors, continue processing (default in dev)
"critical_errors" Only throw errors marked as critical
"all_errors" Throw on any error

Errors marked for panic carry Symbol.for('vite-plugin-react-server.panic'). Use isPanic(error) or assertPanic(error) to check/throw.

Error Flow

Stream errors (RSC + HTML rendering)

createRenderToPipeableStreamHandler.server.ts is the main consumer. React's onError and onShellError callbacks feed into handleError():

React onError/onShellError
  → handleError({ error, errorInfo, panicThreshold, critical })
    → toError(error)         — normalize unknown → Error
    → logError(err, logger)  — format and log via Vite logger
    → if should panic: mark with PANIC_SYMBOL, return error to throw
    → else: return null (continue)

Worker errors (production builds)

Workers serialize errors across the thread boundary:

Worker thread: serializeError(error) + serializeErrorInfo(errorInfo)
  → post message to main thread
Main thread: toError(serialized) → handleError(...)

serializeError extracts message, stack, name, cause, breadcrumbs and the panic symbol into a plain object. serializeErrorInfo extracts React's componentStack and digest.

Directive errors (compile time)

createTransformer.ts checks panicThreshold:

  • "none" in dev: directive warnings logged, not thrown
  • Otherwise: throw new Error(warning.message)

Error constants live in directiveError.ts (DIRECTIVE_ERRORS.FILE_LEVEL, DIRECTIVE_ERRORS.FUNCTION_LEVEL).

Global handler

setupGlobalErrorHandler (used in configureRequestHandler.client.ts) attaches uncaughtException and unhandledRejection handlers during dev server lifetime. cleanupGlobalErrorHandler removes them on server close.

Issue: cleanupGlobalErrorHandler calls process.removeAllListeners("uncaughtException") — this removes ALL listeners, not just the ones it added. Could strip other handlers (Vite's own, test frameworks, etc.).

Core Utilities

Module Purpose
toError.ts Normalize any thrown value into an Error — handles strings, nulls, empty objects, serialized worker errors, nested .error/.reason shapes
handleError.ts Log + panic decision. Deduplicates repeated identical errors.
logError.ts Format error for Vite logger (dev: full stack, prod: message only)
enhanceError.ts Wrap errors with [context:error] prefix and cause chain. Currently unused.
assertPanic.ts asserts error — rethrows if error has panic symbol. Currently unused.
shouldPanic.ts Pure logic: should this error panic given threshold + critical flag?
panicThresholdHandler.ts shouldCausePanic() — used by static generation (renderPages, plugin.server, plugin.client) to decide whether to throw after batch rendering.
serializeError.ts Error → plain object for worker postMessage
serializeErrorInfo.ts React ErrorInfo → plain object for worker postMessage
directiveError.ts Directive error message constants

Unused Code

enhanceError, assertPanic, and createContextualError have been removed (zero consumers).