withastro/astro · error · AstroError

`getActionState()` could not find a state object.

Error message

`getActionState()` could not find a state object.

What it means

Thrown by @astrojs/react's `getActionState()` when the form request is valid but the `_astroActionState` field is absent from the parsed `FormData`. That field is injected by the `withState()` wrapper so the server can recover the prior state passed to `useActionState()`.

Source

Thrown at packages/integrations/react/src/actions.ts:58

	return callback;
}

/**
 * Retrieve the state object from your action handler when using `useActionState()`.
 * To ensure this state is retrievable, use the {@linkcode withState} helper.
 */
export async function getActionState<T>({ request }: { request: Request }): Promise<T> {
	const contentType = request.headers.get('Content-Type');
	if (!contentType || !isFormRequest(contentType)) {
		throw new AstroError(
			'`getActionState()` must be called with a form request.',
			"Ensure your action uses the `accept: 'form'` option.",
		);
	}
	const formData = await request.clone().formData();
	const state = formData.get('_astroActionState')?.toString();
	if (!state) {
		throw new AstroError(
			'`getActionState()` could not find a state object.',
			'Ensure your action was passed to `useActionState()` with the `withState()` wrapper.',
		);
	}
	return JSON.parse(state) as T;
}

const formContentTypes = ['application/x-www-form-urlencoded', 'multipart/form-data'];

function isFormRequest(contentType: string) {
	// Split off parameters like charset or boundary
	// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Type#content-type_in_html_forms
	const type = contentType.split(';')[0].toLowerCase();

	return formContentTypes.some((t) => type === t);
}

/**

View on GitHub (pinned to d081033d5f)

Solutions

  1. Wrap the action with `withState()` before handing it to `useActionState()`: `const [state, dispatch] = useActionState(withState(myAction), initial);`.
  2. Confirm the action is invoked through the wrapper, not the original function reference.
  3. If posting manually, include a `_astroActionState` field (JSON-stringified state) in the FormData.

Example fix

// before
const [state, dispatch] = useActionState(myAction, initialState);
// after
import { withState } from '@astrojs/react/actions';
const [state, dispatch] = useActionState(withState(myAction), initialState);
Defensive patterns

Strategy: validation

Validate before calling

const formData = await request.clone().formData();
if (!formData.has('_astroActionState')) {
  throw new Error('Action not wired through withState(); skipping getActionState.');
}

Try / catch

try { const state = await getActionState({ request }); } catch (e) { if (/could not find a state object/) { /* return initial state */ } else throw e; }

Prevention

When it happens

Trigger: Calling `getActionState` on an action that was NOT wrapped with `withState()` before being passed to React's `useActionState()`. A hand-built form that posts to the action without the hidden `_astroActionState` input. The wrapper's `formData.set('_astroActionState', ...)` was skipped because the callback branch returned early.

Common situations: Passing the raw action to `useActionState` instead of `withState(action)`. Using a custom form submission that bypasses React's progressive-enhancement hidden field injection.

Related errors


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