Guides
Troubleshooting
How to read compiler diagnostics, what the common ones mean, and how to decode production error codes.
Compiler diagnostics include a source location, an error code, and a message. This page explains common errors and how to resolve them.
Reading a diagnostic
A compile error looks like this:
src/components/Profile.tsx:14:5: UnsupportedSyntax: React class components are unsupported; use a function component and Vidact errorBoundaryIt has three parts: the location (file:line:column), which points at the expression the compiler stopped on; the code, which names the kind of problem; and the message, which usually says what to do instead. Vite shows the same information in its error overlay.
| Code | Meaning |
|---|---|
UnsupportedSyntax | The construct is outside Vidact's React subset, or needs a feature flag that is not enabled |
DestructiveRenderMutation | The component body writes to something it should only read |
UnsupportedControlFlow, AnalysisFailed | The compiler could not follow the control flow of the component |
UnsupportedComponentForm | Something was used as a component that the compiler cannot treat as one |
CyclicUpdaterGraph, MultipleSourceWriters | Two updates depend on each other, or two places write the same derived value |
Common diagnostics
"requires the async feature" (or another feature)
You used an API from an opt-in family. Add the named feature to vidact({ features: [...] }). See Opt-in features.
Class component
Convert to a function component. For error boundaries use errorBoundary; see Error handling.
Assignment to a prop, a prop's property, or an outer variable
export function Label({ text }) { text = text.trim() return <span>{text}</span>}Introduce a new binding instead: const trimmed = text.trim(). For module-level variables, move the write into an event handler or effect.
Unsupported useState call
useState (and the other hooks) must be called directly in a component or in a custom hook defined in the same file. A hook defined in another module is not yet expanded. Move the hook next to its caller, or inline it.
Unsupported list key
Keys must be a plain identifier or member expression, such as item.id. A computed key like `${item.id}-row` is rejected; give the item a real ID.
Unknown event prop
A prop that starts with on must be a known DOM event. Check the spelling (onDoubleClick, not onDblClick). Custom element events are not yet supported as props; add them with addEventListener in a ref callback.
Array destructuring from an unknown call
const [value] = createTuple()The compiler treats array destructuring from a function call as a possible hook call and needs to know what it is. Destructure a local variable instead, or index into the result.
try / finally around rendering
Rendering inside try blocks that also throw or use finally cannot be lowered. Move the risky code into an event handler or effect and keep the component body to straight-line construction.
Dependency failed to compile
A package you import uses code outside the supported subset. Update or replace the affected component, or report the unsupported form with a minimal reproduction. Excluding a React component package from compilation does not make it work in Vidact. includeDependencies is only for compatible packages that were not detected automatically; see @vidact/vite.
Runtime errors
Protocol mismatch on startup
The runtime checks that the compiler and runtime agree on a protocol version. If you see an error mentioning vidact-runtime-v2, your @vidact/runtime, @vidact/vite, and @vidact/start versions do not match. Align them in package.json and reinstall.
Duplicate key
Two items in a list share a key. Vidact detects this before changing the DOM and throws, so the list is left in its previous state. Fix the key source.
Hydration mismatch
The server and client produced different output for the same route. Vidact repairs what it can and reports through onRecoverableError. The usual causes are:
- Reading browser-only globals such as
windowduring construction. - Non-deterministic values such as
Date.now()orMath.random()in the component body. - A different set of feature flags or a different package version between the server and client builds.
- An external store whose
getServerSnapshotdisagrees with the server's actual value.
Update did not stabilize
An effect or store subscription writes state that triggers the same effect again, forever. Vidact stops after a bounded number of passes and throws rather than hanging the tab. Look for an effect that sets state it also depends on without a guard.
Production error codes
To keep bundles small, production builds replace runtime error messages with short codes such as V025. The codes are stable within a runtime version. To decode one, check the error catalog for the exact @vidact/runtime version you shipped; the mapping is documented in the package's changelog and in the repository under docs/architecture/development-diagnostics-and-production-error-codes.md.
Getting help
If a diagnostic seems wrong, or a pattern that should be in the subset is rejected, open an issue with the smallest component that reproduces it. The compiler's test suite is built from examples like that.