withastro/astro · error · ActionError
UNSUPPORTED_MEDIA_TYPE
UNSUPPORTED_MEDIA_TYPE
Error message
This action only accepts FormData.
What it means
The action was defined with accept: 'form', so its server handler (getFormServerHandler) requires the input to be a FormData instance. Receiving any other value (plain object, JSON) throws ActionError code UNSUPPORTED_MEDIA_TYPE (HTTP 415).
Source
Thrown at packages/astro/src/actions/runtime/server.ts:80
Object.assign(safeServerHandler, {
orThrow(this: ActionAPIContext, unparsedInput: unknown) {
if (typeof this === 'function') {
throw new AstroError(ActionCalledFromServerError);
}
return serverHandler(unparsedInput, this);
},
});
return safeServerHandler as ActionClient<TOutput, TAccept, TInputSchema> & string;
}
function getFormServerHandler<TOutput, TInputSchema extends z.$ZodType>(
handler: ActionHandler<TInputSchema, TOutput>,
inputSchema?: TInputSchema,
) {
return async (unparsedInput: unknown, context: ActionAPIContext): Promise<Awaited<TOutput>> => {
if (!(unparsedInput instanceof FormData)) {
throw new ActionError({
code: 'UNSUPPORTED_MEDIA_TYPE',
message: 'This action only accepts FormData.',
});
}
if (!inputSchema) return await handler(unparsedInput, context);
const parsed = await parseFormInput(inputSchema, unparsedInput);
if (!parsed.success) {
throw new ActionInputError(parsed.error.issues);
}
return await handler(parsed.data, context);
};
}
async function parseFormInput(inputSchema: z.$ZodType, unparsedInput: FormData) {
const baseSchema = unwrapBaseZ4ObjectSchema(inputSchema, unparsedInput);View on GitHub (pinned to d081033d5f)
Solutions
- Call the action with a real FormData object (e.g. new FormData(formElement)).
- If you want JSON/RPC calls, remove accept: 'form' or add a separate JSON action.
- Use an HTML <form> posting to the action for the form-accept path.
Example fix
// before
export const subscribe = defineAction({
accept: 'form',
input: z.object({ email: z.string().email() }),
handler: async (i) => i,
});
// caller
await actions.subscribe({ email: 'a@b.c' }); // throws 415
// after
const fd = new FormData();
fd.set('email', 'a@b.c');
await actions.subscribe(fd); Defensive patterns
Strategy: type-guard
Validate before calling
// Ensure the value is FormData before calling a form-accept action.
if (!(payload instanceof FormData)) {
throw new Error('This action requires a FormData instance; use accept:"form".');
} Type guard
function isFormData(v: unknown): v is FormData {
return typeof FormData !== 'undefined' && v instanceof FormData;
} Try / catch
try {
await actions.subscribe(payload);
} catch (e) {
if (e instanceof ActionError && e.code === 'UNSUPPORTED_MEDIA_TYPE') {
// build a FormData and retry, or guide the user to use a form
} else throw e;
} Prevention
- Match call style to the action's accept mode (FormData for 'form', objects for JSON).
- Build FormData from the form element with new FormData(formEl).
- Keep a single action either form- or JSON-accepting to avoid mismatch.
When it happens
Trigger: A form-accept action invoked via RPC/JSON (e.g. actions.myForm({ email }) with a plain object from the client), or called programmatically on the server with a non-FormData value.
Common situations: Declaring an action as form-only but calling it like a normal JSON action from client code; unit-testing the action with a plain object instead of FormData.
Related errors
- BAD_REQUEST
- Expected handler for action ${pathKeys.join('.')} to be a fu
- [astro:actions] `defineAction()` unexpectedly used on the cl
- [astro:actions] `getActionContext()` unexpectedly used on th
- BAD_REQUEST
AI-assisted analysis of withastro/astro@d081033d5f (2026-08-12).
Data as JSON: /api/errors/1eec47016b735560.
Report an issue: GitHub.