{"record":{"id":"2875fe82b4211a28","repo":"withastro/astro","slug":"getactionstate-could-not-find-a-state-object","errorCode":null,"errorMessage":"`getActionState()` could not find a state object.","messagePattern":"`getActionState\\(\\)` could not find a state object\\.","errorType":"exception","errorClass":"AstroError","httpStatus":null,"severity":"error","filePath":"packages/integrations/react/src/actions.ts","lineNumber":58,"sourceCode":"\treturn callback;\n}\n\n/**\n * Retrieve the state object from your action handler when using `useActionState()`.\n * To ensure this state is retrievable, use the {@linkcode withState} helper.\n */\nexport async function getActionState<T>({ request }: { request: Request }): Promise<T> {\n\tconst contentType = request.headers.get('Content-Type');\n\tif (!contentType || !isFormRequest(contentType)) {\n\t\tthrow new AstroError(\n\t\t\t'`getActionState()` must be called with a form request.',\n\t\t\t\"Ensure your action uses the `accept: 'form'` option.\",\n\t\t);\n\t}\n\tconst formData = await request.clone().formData();\n\tconst state = formData.get('_astroActionState')?.toString();\n\tif (!state) {\n\t\tthrow new AstroError(\n\t\t\t'`getActionState()` could not find a state object.',\n\t\t\t'Ensure your action was passed to `useActionState()` with the `withState()` wrapper.',\n\t\t);\n\t}\n\treturn JSON.parse(state) as T;\n}\n\nconst formContentTypes = ['application/x-www-form-urlencoded', 'multipart/form-data'];\n\nfunction isFormRequest(contentType: string) {\n\t// Split off parameters like charset or boundary\n\t// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Type#content-type_in_html_forms\n\tconst type = contentType.split(';')[0].toLowerCase();\n\n\treturn formContentTypes.some((t) => type === t);\n}\n\n/**","sourceCodeStart":40,"sourceCodeEnd":76,"githubUrl":"https://github.com/withastro/astro/blob/52e6c34790cc8ac4e69e6135ace06049867e5c4a/packages/integrations/react/src/actions.ts#L40-L76","documentation":"After the form Content-Type check passes, getActionState() looks for the _astroActionState form field that React's useActionState adds when the action is wrapped with the adapter's withState() helper. If the field is absent, the form was submitted without that wrapper, so there is no state payload to deserialize.","triggerScenarios":"Passing the raw action to useActionState instead of withState(action); submitting the action through a plain <form action={action}> with no useActionState; a hand-crafted POST that omits the hidden _astroActionState field.","commonSituations":"Adopting getActionState on an existing form flow that never used useActionState; forgetting that withState is what injects the serialized state into the payload; React 18 setups where useActionState is unavailable.","solutions":["Wrap the action before passing it to useActionState: useActionState(withState(action), initialState)","Read the state in the handler via getActionState({ request }) only in actions that are used with useActionState","Do not call getActionState from actions that plain forms submit directly"],"exampleFix":"// before\nconst [state, formAction] = useActionState(myAction, null);\n// after\nimport { withState } from '@astrojs/react/actions';\nconst [state, formAction] = useActionState(withState(myAction), null);","handlingStrategy":"validation","validationCode":"const fd = await request.clone().formData();\nif (!fd.has('_astroActionState')) {\n  // Submitted without withState()/useActionState; nothing to read\n  return null;\n}\nconst state = await getActionState({ request });","typeGuard":"async function hasActionState(request: Request): Promise<boolean> {\n  const fd = await request.clone().formData();\n  return typeof fd.get('_astroActionState') === 'string';\n}","tryCatchPattern":"let state: T | null = null;\ntry {\n  state = await getActionState<T>({ request });\n} catch (err) {\n  if (String(err?.message).includes('could not find a state object')) {\n    state = null; // form posted without withState wrapper\n  } else throw err;\n}","preventionTips":["Always wrap actions with withState() before useActionState()","Keep a single helper that pairs withState and getActionState so they cannot drift","Test the full form round-trip, not just the handler"],"tags":["react","actions","use-action-state","forms"],"backgroundTag":"missing-form-field","analyzedSha":"52e6c34790cc8ac4e69e6135ace06049867e5c4a","analyzedAt":"2026-08-18T18:48:03.901Z","contentChangedAt":"2026-08-18T18:48:03.901Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}