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

  1. Store only plain serializable data (primitives, plain objects, arrays) in locals.
  2. Convert Map/Set to plain objects/arrays before storing; serialize Date to ISO strings.
  3. 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

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.