withastro/astro · error · Error
The passed value can't be serialized.
Error message
The passed value can't be serialized.
What it means
Thrown by trySerializeLocals when the locals value contains non-serializable types. Locals are serialized (JSON.stringify) for transmission to client components or across the SSR boundary; types like functions, Map, Set, Date, class instances, or circular references break JSON.stringify and are rejected up front by isLocalsSerializable.
Source
Thrown at packages/astro/src/core/middleware/index.ts:205
return proto === baseProto;
}
/**
* It attempts to serialize `value` and return it as a string.
*
* ## Errors
* If the `value` is not serializable if the function will throw a runtime error.
*
* Something is **not serializable** when it contains properties/values like functions, `Map`, `Set`, `Date`,
* and other types that can't be made a string.
*
* @param value
*/
function trySerializeLocals(value: unknown) {
if (isLocalsSerializable(value)) {
return JSON.stringify(value);
} else {
throw new Error("The passed value can't be serialized.");
}
}
// NOTE: this export must export only the functions that will be exposed to user-land as officials APIs
export { createContext, sequence, trySerializeLocals };
export { defineMiddleware } from './defineMiddleware.js';
View on GitHub (pinned to d081033d5f)
Solutions
- Store only plain serializable data (primitives, plain objects, arrays) in locals.
- Convert Map/Set to plain objects/arrays before storing; serialize Date to ISO strings.
- Move non-serializable resources (connections, functions) to a module-level cache or Symbol keyed property that serialization skips.
Example fix
// before
Astro.locals.session = userSession; // class instance
Astro.locals.roles = new Set(['admin']);
// after
Astro.locals.session = { userId: userSession.userId };
Astro.locals.roles = ['admin']; Defensive patterns
Strategy: type-guard
Validate before calling
// Validate serializability before storing.
function isSerializable(v: unknown): boolean {
if (v === null || ['string','number','boolean'].includes(typeof v)) return true;
if (Array.isArray(v)) return v.every(isSerializable);
if (typeof v === 'object') {
return Object.values(v).every(isSerializable);
}
return false; // functions, Map, Set, Date, Symbol, undefined
}
if (!isSerializable(value)) throw new Error('not serializable');
Astro.locals.data = value; Type guard
function isLocalsSerializable(value: unknown): boolean {
if (value === null) return true;
const t = typeof value;
if (t === 'function' || t === 'symbol' || t === 'undefined') return false;
if (t !== 'object') return true;
if (value instanceof Map || value instanceof Set || value instanceof Date) return false;
return Object.values(value).every(isLocalsSerializable);
} Try / catch
try {
trySerializeLocals(Astro.locals);
} catch (e) {
if (e instanceof Error && /can't be serialized/i.test(e.message)) {
// strip non-serializable values from locals
} else throw e;
} Prevention
- Store only plain JSON-safe data in locals.
- Convert Map/Set to plain objects/arrays and Date to ISO strings before storing.
- Keep DB connections/functions on module-scoped caches, not in locals.
When it happens
Trigger: Assigning functions, Map, Set, Date, class instances, Symbols, or circular references to Astro.locals/context.locals, then triggering serialization (e.g. passing locals to client islands, server actions, or an adapter that serializes state). trySerializeLocals runs isLocalsSerializable and throws a plain Error if it returns false.
Common situations: Storing a class instance or a DB connection in locals; putting Map/Set collections in locals; assigning a function/callback to locals; circular object graphs from ORMs or session objects.
Related errors
AI-assisted analysis of withastro/astro@d081033d5f (2026-08-12).
Data as JSON: /api/errors/026fb63dac57ce15.
Report an issue: GitHub.