Diagnostics
LUM4011: Non-serializable scoped state value
A scoped state (defineState) that the page sends to the browser holds a value that is not plain data.
Severity: error · Category: islands and boundaries (4xxx)
Reported by: the server runtime, with the path of the offending value, when the page sends the
state: the shell fails (the error page renders), or a streamed boundary keeps its fallback and the
error goes to onError.
#What it means
A state defined with defineState() that islands or SPA routes import is sent to the browser with
the page: the server's values replace what the factory creates in the browser, so islands start
from the same values the server rendered. Only the signals and stores the factory returns are sent,
and they must hold plain data, like island props: strings, numbers, booleans, null, arrays, plain
objects, Date, Map, Set, BigInt and URL. A class instance, a function or a promise cannot
be.
#Example
// lib/session.ts, imported by an island
import { defineState, isServer, locals, signal } from "lumiose";
export const session = defineState(() => ({
// A class instance from the auth library: not plain data
user: signal(isServer ? locals().user : null),
}));#How to fix it
- Keep plain data in the state's signals and stores:
signal(isServer ? { id: user.id, name: user.name } : null). - Derive the rest: computeds and functions the factory returns are not sent; they are created again when the factory runs in the browser.
- State that only server code uses belongs in a module that no island imports, or in a server-only
module (
*.server.ts): it is never sent.
Everything a sent state holds is visible in the page; never put secrets in it.
#Related
- Sharing state
- Islands
- LUM4002: non-serializable island prop