#Netlify Form handling
14 messages · Page 1 of 1 (latest)
Hi @indigo pivot. I'm kapa.ai, your friendly support bot. I will try to answer your question by searching through the knowledge base. :wave:
Astro’s docs don’t describe your exact 500 error scenario, so some of this will be necessarily high‑level. But there are a few deployment‑specific requirements and tools you can check.
-
Confirm the page is on-demand rendered (not prerendered)
Actions only work with server output. If the page containing your form is prerendered, calling an action will fail at runtime. Ensure prerendering is disabled on that page (or globally use a server adapter like@astrojs/netlifyand mark the page for on‑demand rendering). [Actions error list; Actions must use server output] -
Use the HTML form → action pattern exactly as documented
The pattern you’re using matches the docs: a<form method="POST" action={actions.someAction}>that posts directly to the action. [Call actions from form]
Example from the docs:--- import { actions } from 'astro:actions'; --- <form method="POST" action={actions.newsletter}> <label> E-mail <input required type="email" name="email" /> </label> <button>Sign up</button> </form>Double‑check that:
- The action is exported correctly from
src/actions/index.ts(e.g.export const server = { form: defineAction({ … }) }). - The name you use in
actions.formmatches the key inserver.
- The action is exported correctly from
-
Check for action‑specific runtime errors
Actions can fail due to validation or handler errors, which will surface as 500s. The docs recommend always checkingerrorvsdatawhen you read the result withAstro.getActionResult(). [Handling returned data]
If you’re usingaccept: 'form', make sure your Zod schema matches the actual form fields and types. [Accepting form data; Validating form data] -
Netlify‑specific behavior (skew protection & sessions)
- The Netlify adapter automatically injects a deploy ID header into internal requests for actions, which prevents version skew during deploys. [Netlify skew protection]
- If you’re using the advanced POST/Redirect/GET pattern with middleware and Netlify Blob (for persisting action results), ensure that middleware is deployed and that Blob access is configured correctly; otherwise, the middleware path can throw 500s. [Persist action results with session]
- How to debug further (within Astro’s patterns)
The docs don’t give a direct recipe for Netlify 500 debugging, but you can:- Temporarily simplify your action handler to a trivial success (e.g. return a static object) to see if the 500 is caused by your email‑sending code vs. the action wiring itself.
- Use
Astro.getActionResult(actions.form)on the page to render anyerrormessage to the UI, which can help distinguish validation vs. internal errors. [Handle form action errors]
Because the knowledge sources don’t include Netlify‑specific 500 troubleshooting steps or logs, I can’t say exactly why your preview deploy fails while dev works. But based on the docs, the first things to verify are:
- The page is not prerendered and is using a server adapter.
- The action is defined and exported correctly and uses
accept: 'form'if you’re posting form data. - Any advanced middleware/session logic (if you’re using it) is correctly configured on Netlify.
Note: you can ask me a follow up question by @ mentioning me again :speech_balloon:
@indigo pivot Kapa.ai is still learning and improving, please let me know how I did by reacting below.
my contact.astro page has export const prerender = false; declared.
I have adapter: netlify() setup in my astro.config.mjs as well.
Give me the code to render any error messages on the UI please @heavy lark
Let me check the knowledge base for you again. One moment! :books:
Here’s a minimal pattern using Astro.getActionResult() to show any errors from your form action on the contact page UI.
Assuming you have an action named form in src/actions/index.ts, your src/pages/contact.astro can look like this:
---
import { actions, isInputError } from 'astro:actions';
export const prerender = false;
const result = Astro.getActionResult(actions.form);
const inputErrors = isInputError(result?.error) ? result.error.fields : {};
---
{result?.error && !isInputError(result.error) && (
<p class="error">Something went wrong. Please try again later.</p>
)}
<form class="form" method="POST" action={actions.form} novalidate>
<label>
Email
<input
type="email"
name="email"
aria-describedby="email-error"
required
/>
</label>
{inputErrors.email && (
<p id="email-error" class="error">
{inputErrors.email.join(', ')}
</p>
)}
<!-- add your other fields here, using inputErrors.fieldName similarly -->
<button type="submit">Send</button>
</form>
This does two things:
- Shows a general error message when the action fails for any non‑validation reason. [Handle form errors]
- Uses
isInputError()to render field‑specific validation messages (example shown foremail). [Displaying input errors]
Note: you can ask me a follow up question by @ mentioning me again :speech_balloon:
@indigo pivot Kapa.ai is still learning and improving, please let me know how I did by reacting below.
This just gives me a general error message though. I wont be able to do anything with that information @heavy lark
Let me check the knowledge base for you again. One moment! :books:
You can surface the actual ActionError details (code/message) and, for validation errors, the per‑field messages. Astro exposes all of this on the action result; you just need to render it.
1. Show full ActionError details
---
import { actions, isInputError } from 'astro:actions';
export const prerender = false;
const result = Astro.getActionResult(actions.form);
const inputErrors = isInputError(result?.error) ? result.error.fields : {};
---
{result?.error && (
<div class="error-block">
<p><strong>Action failed</strong></p>
<p><code>code:</code> {result.error.code}</p>
{result.error.message && (
<p><code>message:</code> {result.error.message}</p>
)}
</div>
)}
<form class="form" method="POST" action={actions.form} novalidate>
<!-- example field -->
<label>
Email
<input
type="email"
name="email"
aria-describedby="email-error"
required
/>
</label>
{inputErrors.email && (
<p id="email-error" class="error">
{inputErrors.email.join(', ')}
</p>
)}
<button type="submit">Send</button>
</form>
Astro.getActionResult(actions.form)gives you theerrorobject from the action. [Handle form action errors]- That
erroris anActionErrorwith.codeand optional.message, which you can render directly. [ActionError]
2. See all validation errors
If your action uses input: z.object({ ... }), isInputError() lets you inspect all field errors:
---
import { actions, isInputError } from 'astro:actions';
export const prerender = false;
const result = Astro.getActionResult(actions.form);
const inputErrors = isInputError(result?.error) ? result.error.fields : {};
---
{isInputError(result?.error) && (
<pre class="debug">
{JSON.stringify(result.error.fields, null, 2)}
</pre>
)}
<form method="POST" action={actions.form} novalidate>
<!-- repeat this pattern for each field -->
<label>
Name
<input name="name" aria-describedby="name-error" />
</label>
{inputErrors.name && (
<p id="name-error" class="error">
{inputErrors.name.join(', ')}
</p>
)}
<button>Send</button>
</form>
`result.error.fields` contains an array of messages per field name, which you can both debug with `<pre>` and wire into your UI. [[Displaying form input errors](<https://docs.astro.build/en/guides/actions/#displaying-form-input-errors>); [isInputError](<https://docs.astro.build/en/reference/modules/astro-actions/#isinputerror>)]
----------------------
Note: you can ask me a follow up question by @ mentioning me again :speech_balloon:
----------------------