withastro/astro · error · ActionInputError

BAD_REQUEST

BAD_REQUEST

Error message

Failed to validate: ${JSON.stringify(issues, null, 2)}

What it means

After converting FormData against the action's zod input schema (parseFormInput), validation failed. The handler throws ActionInputError carrying the zod issues, surfaced to the caller as ActionError code BAD_REQUEST (400).

Source

Thrown at packages/astro/src/actions/runtime/server.ts:91

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);
	const input =
		baseSchema instanceof z.$ZodObject
			? formDataToObject(unparsedInput, baseSchema)
			: unparsedInput;

	const parsed = await z.safeParseAsync(inputSchema, input);
	return parsed;
}

function getJsonServerHandler<TOutput, TInputSchema extends z.$ZodType>(
	handler: ActionHandler<TInputSchema, TOutput>,

View on GitHub (pinned to d081033d5f)

Solutions

  1. Inspect the error with isInputError(error) and read error.fields to display per-field messages.
  2. Align the form input names with the zod schema keys (including nested prefixes).
  3. Add matching client-side validation so invalid forms are caught before submission.

Example fix

// before - schema wants a number age, form sends string and is missing it
const input = z.object({ name: z.string(), age: z.number() });
// after - use coercion + optional handling, and check error.fields on the client
const input = z.object({
  name: z.string().min(1),
  age: z.coerce.number().int().nonnegative(),
});
// client
const { data, error } = await actions.signup.orThrow(fd);
if (isInputError(error)) showError(error.fields);
Defensive patterns

Strategy: try-catch

Validate before calling

// Validate form input against the same zod schema on the client before calling.
const parsed = subscribeSchema.safeParse(Object.fromEntries(new FormData(formEl)));
if (!parsed.success) showFieldErrors(parsed.error.issues);

Type guard

// On the result, narrow to an input error to read field issues.
function isInputError(e: unknown): e is { fields: Record<string, string[]> } {
  return e instanceof ActionError && (e as any).fields !== undefined;
}

Try / catch

const { data, error } = await actions.signup(fd);
if (isInputError(error)) {
  for (const [field, msgs] of Object.entries(error.fields)) showFieldError(field, msgs.join(', '));
}

Prevention

When it happens

Trigger: A form submission whose fields do not satisfy the schema: missing required fields, wrong types, failed refinements, or field names that do not match schema keys.

Common situations: User submits an incomplete form; HTML input names differ from zod keys; type mismatch (string where number expected); file field not allowed by schema.

Related errors


AI-assisted analysis of withastro/astro@d081033d5f (2026-08-12). Data as JSON: /api/errors/49fc73ba189a24d2. Report an issue: GitHub.