What Causes This Error?
This error occurs when an action handler returns data that can’t be serialized. Actions communicate between server and client, so return values must be JSON-serializable.
The Problem
// src/actions/index.ts
import { defineAction, z } from 'astro:actions';
export const server = {
getUser: defineAction({
handler: async () => {
// ❌ Functions can't be serialized
return {
name: 'John',
greet: () => 'Hello!', // Invalid!
};
},
}),
getDate: defineAction({
handler: async () => {
// ❌ Dates need special handling
return new Date(); // Not directly serializable
},
}),
};
The Fix
Return Serializable Data
// src/actions/index.ts
import { defineAction, z } from 'astro:actions';
export const server = {
getUser: defineAction({
handler: async () => {
// ✅ Only serializable data
return {
name: 'John',
greeting: 'Hello!', // String instead of function
};
},
}),
getDate: defineAction({
handler: async () => {
// ✅ Convert to serializable format
return {
date: new Date().toISOString(),
};
},
}),
};
Common Scenarios
Valid Return Types
export const server = {
validAction: defineAction({
handler: async () => {
// ✅ All valid return types
return {
string: 'hello',
number: 42,
boolean: true,
null: null,
array: [1, 2, 3],
nested: { a: { b: 'c' } },
date: new Date().toISOString(), // String format
};
},
}),
};
Invalid Return Types
export const server = {
invalidAction: defineAction({
handler: async () => {
// ❌ These will cause errors
return {
func: () => {}, // Functions
symbol: Symbol('test'), // Symbols
map: new Map(), // Maps
set: new Set(), // Sets
circular: null, // Circular references
undefined: undefined, // Undefined values
};
},
}),
};
Converting Complex Types
export const server = {
getData: defineAction({
handler: async () => {
// Convert Map to array
const map = new Map([['a', 1], ['b', 2]]);
const mapArray = Array.from(map.entries());
// Convert Set to array
const set = new Set([1, 2, 3]);
const setArray = Array.from(set);
// Convert Date to string
const date = new Date();
const dateString = date.toISOString();
return {
map: mapArray,
set: setArray,
date: dateString,
};
},
}),
};
Database Results
export const server = {
getProducts: defineAction({
handler: async () => {
const products = await db.query('SELECT * FROM products');
// ✅ Transform to plain objects
return products.map(p => ({
id: p.id,
name: p.name,
price: p.price,
createdAt: p.createdAt.toISOString(), // Date to string
}));
},
}),
};
Class Instances
class User {
constructor(public name: string, public email: string) {}
greet() {
return `Hello, ${this.name}`;
}
}
export const server = {
getUser: defineAction({
handler: async () => {
const user = new User('John', 'john@example.com');
// ✅ Extract data from class instance
return {
name: user.name,
email: user.email,
greeting: user.greet(), // Call method, return result
};
},
}),
};
Handling Errors
export const server = {
riskyAction: defineAction({
handler: async () => {
try {
const result = await fetchData();
// ✅ Ensure result is serializable
return JSON.parse(JSON.stringify(result));
} catch (error) {
// ✅ Return error as string
return {
error: error instanceof Error ? error.message : 'Unknown error',
};
}
},
}),
};
Testing Serializability
function isSerializable(value: unknown): boolean {
try {
JSON.stringify(value);
return true;
} catch {
return false;
}
}
export const server = {
debugAction: defineAction({
handler: async () => {
const result = await getSomeData();
// Debug: check if serializable
if (!isSerializable(result)) {
console.error('Non-serializable data:', result);
throw new Error('Data is not serializable');
}
return result;
},
}),
};
BigInt Handling
export const server = {
getLargeNumber: defineAction({
handler: async () => {
const bigNumber = BigInt(9007199254740991);
// ✅ Convert BigInt to string
return {
value: bigNumber.toString(),
};
},
}),
};
Quick Checklist
- Return only JSON-serializable data
- Convert Dates to ISO strings
- Don’t return functions or methods
- Convert Maps/Sets to arrays
- Stringify BigInt values
- Avoid circular references