Skip to content
Vincle

Loading…

    Error handling

    Errors throw or reject bare. There is no component-level boundary that catches them mid-tree: once HTML is sent, nothing can replace it, so a server error is a 500 or a fallback page. For streaming, @vincle/flow recovers per fragment with onError.

    Top-level errors#

    Wrap renderToString in try/catch for a global fallback:

    import { renderToString } from "@vincle/core";
    try {
    const html = await renderToString(<Page />);
    // send html to client
    } catch (error) {
    console.error("Page render failed:", error);
    // send a 500 or fallback page
    }

    The try catches errors both while <Page /> is constructed and while it renders; a .catch() on the returned promise misses the first kind.

    An Error thrown by a component is prefixed with the name of the innermost component that threw: [Profile] not found. Any other thrown value reaches catch exactly as thrown.

    Message format#

    Every error vincle raises says where it came from, what went wrong, why, and how to fix it:

    [vincle/<package>] <api or component>: <what happened>. <why>. <how to fix it>
    [vincle/flow] <Defer target="hero">: merge="prepend" is not supported by this adapter: it supports: replace, append. Pick one of those, or use an adapter that supports "prepend".

    The prefix ([vincle/core], [vincle/flow], [vincle/vite-plugin], [vincle/precompile]) is stable, for filtering logs. In code, read error.code instead of the message.

    Error codes#

    Every error the libraries throw at render or setup carries a stable code, following Node's convention. The type alone cannot tell a vincle refusal from a bug in your own component:

    try {
    html = await renderToString(<Page />);
    } catch (error) {
    if (error.code === "ERR_VINCLE_VOID_CHILDREN") {
    // the markup is wrong, not the data
    }
    }

    The codes are not exported: compare against the string, as you would an ENOENT. The set may grow; an existing code will not be renamed or reused.

    @vincle/core#

    Code Raised when
    ERR_VINCLE_INVALID_TAG a tag name that cannot be written as HTML
    ERR_VINCLE_VOID_CHILDREN content inside a void element (<br>, <img>, …)
    ERR_VINCLE_DANGEROUS_HTML dangerouslySetInnerHTML.__html is not a string
    ERR_VINCLE_FUNCTION_ATTR a function passed as an attribute value
    ERR_VINCLE_VNODE_AS_TEXT a component interpolated where text belongs
    ERR_VINCLE_SCOPE_COLLISION overlapping renders or Scope.with() with no AsyncLocalStorage
    ERR_VINCLE_NO_STORE the Scope store was read before Scope.with() ran
    ERR_VINCLE_NO_SCOPE a Scope call outside any Scope.with()
    ERR_VINCLE_CONTEXT_KEY Scope.key() given something other than a non-empty string
    ERR_VINCLE_CONTEXT_LIMIT more distinct context keys than the table holds
    ERR_VINCLE_CONTEXT_UNSET Scope.get() read a value never set in scope
    ERR_VINCLE_CONTEXT_CHILDREN a context Consumer whose children is not a function

    The first three are TypeErrors; every other code on this page is a plain Error.

    @vincle/flow#

    Code Raised when
    ERR_VINCLE_FLOW_CONFIG an option rejected at the entry point that took it
    ERR_VINCLE_FLOW_NO_ADAPTER a placeholder or fragment with no adapter configured
    ERR_VINCLE_FLOW_MERGE_UNSUPPORTED a merge the adapter cannot express
    ERR_VINCLE_FLOW_FRAGMENT_ID a fragment id that cannot be a DOM id or URL segment
    ERR_VINCLE_FLOW_DUP_FRAGMENT two <Defer> on the same target in one render
    ERR_VINCLE_FLOW_NO_FRAGMENT renderFragment produced nothing for that id
    ERR_VINCLE_FLOW_NO_STREAMING renderToStream given an adapter that cannot stream

    @vincle/vite-plugin and @vincle/precompile#

    Code Raised when
    ERR_VINCLE_VITE_MANIFEST_READ the Vite manifest could not be read
    ERR_VINCLE_VITE_MANIFEST_PARSE the manifest is not JSON
    ERR_VINCLE_VITE_MANIFEST_SHAPE the file parsed but is not a manifest
    ERR_VINCLE_VITE_CONFIG setVite given an option it cannot use
    ERR_VINCLE_VITE_MISSING_ENTRY an entry the manifest does not list
    ERR_VINCLE_PRECOMPILE_CONFIG runtimeSource is not a module specifier
    ERR_VINCLE_PRECOMPILE_HELPER a runtime helper answered a shape the transform cannot use
    ERR_VINCLE_PRECOMPILE_INTERNAL an invariant of the transform broke: a bug to report

    Configuration errors fail fast, at the entry point that receives the option, before anything renders or a build starts. One exception is not an error: an unreadable runtime module makes @vincle/precompile warn and fall back to Deno's output.

    Per-fragment errors (streaming)#

    @vincle/flow takes onError on the render options and on each <Defer>:

    serve(req, () => <Page />, NativeAdapter, {
    onError: (error, { id, kind }) => {
    console.error(`Fragment ${id} failed:`, error);
    return <p>This section failed to load.</p>;
    },
    });

    See renderToStream for details.