withastro/astro · error · Error
[astro:actions] `defineAction()` unexpectedly used on the…
Error message
[astro:actions] `defineAction()` unexpectedly used on the client.
What it means
Astro ships two builds of the astro:actions runtime: a server build where defineAction() registers handlers, and a client build where defineAction() is a stub that throws. If code that defines actions gets bundled for the browser (where no server handler can exist), executing it hits the stub and fails immediately with this message.
Solutions
- In browser code, import only the callable proxy from 'astro:actions' — never relatively import src/actions/index.ts
- Move shared zod schemas and helpers into a separate schema.ts module that both the actions file and client components import
- Check the import chain of the failing file for client directives (<script>, client:*, use client) and cut the actions file out of it
Example fix
// before — ClientComponent.jsx
import { server } from '../../actions/index.js'; // pulls defineAction into browser bundle
// after
import { actions } from 'astro:actions'; // client-safe proxy
import { blogSchema } from '../../actions/schema'; // shared zod schema, no defineAction Defensive patterns
Strategy: validation
Validate before calling
// guard any accidental client-side execution
if (typeof defineAction === 'function' && !import.meta.env.SSR) {
throw new Error('This module defines actions and must not ship to the browser');
} Type guard
// keep definitions physically separated so no guard is needed at runtime: // actions/*.ts (server-only, calls defineAction) vs actions/schema.ts (isomorphic)
Prevention
- Client code imports only { actions } from 'astro:actions'; never relatively import src/actions/index.ts
- Keep zod schemas in a separate isomorphic module both sides can import
- Review client directives (<script>, client:*, 'use client') on any component chain that touches action definitions
When it happens
Trigger: Calling defineAction() inside a <script> tag, a client-imported framework component, or any file that is reached from the browser bundle — most commonly by relatively importing src/actions/index.ts from client code instead of using the astro:actions virtual module.
Common situations: Reusing schema/handler code by importing the actions file into a client component; accidentally adding 'use client'/client:load directives up the import chain of the actions file; bundling shared utilities that transitively import the actions index.
Related errors
- [astro:actions] `getActionContext()` unexpectedly used on…
- ActionCalledFromServerError
- ActionCalledFromServerError
- ServerOnlyModule
- A collision will result in a hard error in following…
AI-assisted analysis of withastro/astro@52e6c34790 (2026-08-18).
Data as JSON: /api/errors/50e7511f6be974f6.
Report an issue: GitHub.
Appendix: source
Thrown at packages/astro/src/actions/runtime/entrypoints/client.ts:28
} from '../client.js';
export { ACTION_QUERY_PARAMS } from '../../consts.js';
export {
ActionError,
isActionError,
isInputError,
} from '../client.js';
export type {
ActionAPIContext,
ActionClient,
ActionErrorCode,
ActionInputSchema,
ActionReturnType,
SafeResult,
} from '../types.js';
export function defineAction() {
throw new Error('[astro:actions] `defineAction()` unexpectedly used on the client.');
}
export function getActionContext() {
throw new Error('[astro:actions] `getActionContext()` unexpectedly used on the client.');
}
export const getActionPath = createGetActionPath({
baseUrl: import.meta.env.BASE_URL,
shouldAppendTrailingSlash,
});
export const actions = createActionsProxy({
handleAction: async (param, path) => {
const headers = new Headers();
headers.set('Accept', 'application/json');
// Apply adapter-specific headers for internal fetches
for (const [key, value] of Object.entries(internalFetchHeaders)) {
headers.set(key, value);View on GitHub (pinned to 52e6c34790)