withastro/astro · error · AstroError
ActionCalledFromServerError
ActionCalledFromServerError
Error message
Action called from a server-rendered page or endpoint without using `Astro.callAction()`. This wrapper must be used to call actions from server code.
What it means
The server-side actions proxy requires per-request state (the SSR manifest and current context) which Astro attaches via an internal fetch-state symbol during request handling. When an action is invoked in server code outside that flow — directly in a page or endpoint body — the state is absent, so Astro throws ActionCalledFromServerError telling you to route the call through Astro.callAction().
Solutions
- Wrap server-side calls: const result = await Astro.callAction(actions.myAction)(input)
- If you need the logic outside a request, extract a plain function and have both the action handler and the caller use it
- Make sure the file executing the call receives the Astro context (a page/endpoint), not a detached module
Example fix
// before — src/pages/dashboard.astro frontmatter const result = await actions.getUser(userId); // throws // after const result = await Astro.callAction(actions.getUser)(userId);
Defensive patterns
Strategy: validation
Validate before calling
// normalize server-side calls through one helper so context is never missing
export async function callFromServer<A extends (input: never) => Promise<unknown>>(
Astro: AstroGlobal,
action: A,
input: Parameters<A>[0],
) {
if (!Astro?.callAction) throw new Error('Not in an Astro request scope');
return Astro.callAction(action)(input);
} Type guard
function hasCallAction(astro: unknown): astro is AstroGlobal {
return typeof astro === 'object' && astro !== null && typeof (astro as AstroGlobal).callAction === 'function';
} Try / catch
try {
const r = await actions.getUser(id);
} catch (e) {
if (e instanceof AstroError && e.code === 'ActionCalledFromServerError') {
// re-route through: await Astro.callAction(actions.getUser)(id)
} else throw e;
} Prevention
- In any server file, always call actions via Astro.callAction(actions.name)(input)
- Never call actions at module scope or outside the request lifecycle; extract shared logic into plain functions instead
- Code-review for bare `actions.` calls in .astro frontmatter and endpoints
When it happens
Trigger: Calling actions.myAction(input) directly in .astro frontmatter or in an API endpoint without the wrapper; invoking the server actions proxy from code that runs outside a request (module scope, background job) where no Astro context exists.
Common situations: Porting client-style action calls into a page; calling actions during prerender/module evaluation; calling an action inside an endpoint to reuse logic instead of extracting the handler.
Related errors
- ActionCalledFromServerError
- [astro:actions] `getActionContext()` unexpectedly used on…
- [astro:actions] `defineAction()` unexpectedly used on the…
- A collision will result in a hard error in following…
- ActionNotFoundError
AI-assisted analysis of withastro/astro@52e6c34790 (2026-08-18).
Data as JSON: /api/errors/75dc6c2ed1f4be54.
Report an issue: GitHub.
Appendix: source
Thrown at packages/astro/src/actions/runtime/entrypoints/server.ts:36
ActionInputSchema,
ActionReturnType,
SafeResult,
} from '../types.js';
export const getActionPath = createGetActionPath({
baseUrl: import.meta.env.BASE_URL,
shouldAppendTrailingSlash,
});
export const actions = createActionsProxy({
handleAction: async (param, path, context) => {
const state: FetchState | undefined = context
? Reflect.get(context, fetchStateSymbol)
: undefined;
if (!state) {
// The context was not created by Astro's request handling (e.g.
// an action invoked from server code without a context).
throw new AstroError(ActionCalledFromServerError);
}
const action = await getAction(state.manifest, path);
if (!action) throw new Error(`Action not found: ${path}`);
return action.bind(context)(param);
},
});
View on GitHub (pinned to 52e6c34790)