withastro/astro · error · AstroError
ActionsReturnedInvalidDataError
ActionsReturnedInvalidDataError
Error message
Action handler returned invalid data. Handlers should return serializable data types like objects, arrays, strings, and numbers. Parse error: ${error} What it means
The action handler's return value is serialized with devalueStringify (devalue) before being sent to the client. If the data is not devalue-serializable (a Response object, a function, an unsupported class instance, Symbols), serialization fails and AstroError ActionsReturnedInvalidDataError is thrown, with an extra hint when a Response was returned.
Source
Thrown at packages/astro/src/actions/runtime/server.ts:514
return {
type: 'empty',
status: 204,
};
}
let body;
try {
body = devalueStringify(res.data, {
// Add support for URL objects
URL: (value) => value instanceof URL && value.href,
});
} catch (e) {
let hint = ActionsReturnedInvalidDataError.hint;
if (res.data instanceof Response) {
hint = REDIRECT_STATUS_CODES.includes(res.data.status as any)
? 'If you need to redirect when the action succeeds, trigger a redirect where the action is called. See the Actions guide for server and client redirect examples: https://docs.astro.build/en/guides/actions.'
: 'If you need to return a Response object, try using a server endpoint instead. See https://docs.astro.build/en/guides/endpoints/#server-endpoints-api-routes';
}
throw new AstroError({
...ActionsReturnedInvalidDataError,
message: ActionsReturnedInvalidDataError.message(String(e)),
hint,
});
}
return {
type: 'data',
status: 200,
contentType: 'application/json+devalue',
body,
};
}
function toArrayBuffer(buffer: Uint8Array): ArrayBuffer {
const copy = new Uint8Array(buffer.byteLength);
copy.set(buffer);
return copy.buffer;
}
View on GitHub (pinned to d081033d5f)
Solutions
- Return plain serializable data: objects, arrays, strings, numbers, booleans, null, Date, URL (supported via the URL plugin), Maps/Sets.
- For redirects, trigger the redirect at the call site (client or Astro.callAction) instead of returning a Response.
- If you truly need to return a Response, use a server endpoint (API route) rather than an action.
- Convert class instances to plain objects before returning.
Example fix
// before
export const go = defineAction({ handler: async () => new Response(null, { status: 302, headers: { Location: '/done' } }) });
// after - return data, redirect at the call site
export const go = defineAction({ handler: async () => ({ ok: true }) });
// caller (client): const { data } = await actions.go(); if (data?.ok) window.location.href = '/done'; Defensive patterns
Strategy: try-catch
Validate before calling
// Sanity-check the handler's return shape in dev before returning it.
function assertSerializable(value: unknown) {
if (value instanceof Response) throw new Error('Actions cannot return a Response; use an endpoint or redirect at the call site.');
if (typeof value === 'function') throw new Error('Actions cannot return a function.');
}
// inside handler:
const result = await doWork(); assertSerializable(result); return result; Type guard
// Reject non-serializable return types up front.
function isSerializableReturn(v: unknown): boolean {
if (v instanceof Response) return false;
if (typeof v === 'function' || typeof v === 'symbol') return false;
return true;
} Prevention
- Return only devalue-supported types (plain objects/arrays/primitives/Date/URL/Map/Set).
- Never return a Response from an action; redirect at the call site instead.
- Convert ORM/class instances to plain objects before returning.
- Keep the devalue support list handy when designing return shapes.
When it happens
Trigger: Handler returns new Response(...) (e.g. trying to redirect), a custom class instance devalue cannot encode, a function, or Symbol-keyed/Symbol-valued structures.
Common situations: Trying to redirect by returning a Response; returning a Mongoose document / ORM model instance; returning a function or Class instance.
Understand the failure class
- Parsing and encoding errors: unexpected token, malformed input — why parsers reject input and how to find the real culprit.
Related errors
- BAD_REQUEST
- UnsupportedConfigTransformError
- ResponseSentError
- [astro:actions] `defineAction()` unexpectedly used on the cl
- [astro:actions] `getActionContext()` unexpectedly used on th
AI-assisted analysis of withastro/astro@d081033d5f (2026-08-12).
Data as JSON: /api/errors/d3d6d6373e1377e8.
Report an issue: GitHub.