withastro/astro · error · Error
[astro:actions] `defineAction()` unexpectedly used on the cl
Error message
[astro:actions] `defineAction()` unexpectedly used on the client.
What it means
The client-side entrypoint of the astro:actions virtual module is a stub that throws if defineAction() is ever executed in a browser bundle. defineAction() is server-only; reaching this stub means server action-definition code leaked into the client module graph (an import that should not exist).
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 d081033d5f)
Solutions
- Move zod input schemas into a separate schemas.ts with no defineAction(), and import only that from client code.
- In client components, import only the type: import type { actions } from '...'; (type-only imports are erased).
- Audit the client module graph (vite build / astro inspect) to confirm no action-definition file reaches the browser.
Example fix
// before - src/actions/index.ts
import { defineAction } from 'astro:actions';
import { z } from 'astro:schema';
const schema = z.object({ email: z.string().email() });
export const subscribe = defineAction({ input: schema, handler: async (i) => i });
// client component imports { schema } from '@/actions' -> pulls defineAction
// after - split schema into src/actions/schemas.ts
import { z } from 'astro:schema';
export const subscribeSchema = z.object({ email: z.string().email() });
// client imports { subscribeSchema } from '@/actions/schemas' (no defineAction) Defensive patterns
Strategy: validation
Validate before calling
// Build-time guard: fail if a client entrypoint imports a module that calls defineAction.
// Example using a simple check in a vite plugin or a lint rule over the client entry graph:
const CLIENT_FORBIDDEN = /defineAction\s*\(/;
// scan files reachable from src/ that are imported by client.entry.ts
for (const file of clientReachableFiles) {
if (CLIENT_FORBIDDEN.test(readFile(file))) {
throw new Error(`${file} calls defineAction() but is reachable from a client entry`);
}
} Prevention
- Split zod schemas into a schemas.ts with no defineAction and import that from the client.
- Use import type on the client so action runtime code is erased.
- Keep server action files out of any barrel the client imports.
When it happens
Trigger: Importing your actions/index.ts (or any module that calls defineAction()) from a client script, a framework island (React/Vue/Svelte client component), or a client entrypoint. The bundler then resolved astro:actions to the client stub.
Common situations: Co-locating a zod schema and defineAction() in one file, then importing that file into a client component to reuse the schema; barrel exports that re-export server action modules; importing actions for "just the types" but pulling runtime code along.
Related errors
- [astro:actions] `getActionContext()` unexpectedly used on th
- Action not found: ${path}
- [RSS] You can only glob entries within 'src/pages/' when pas
- BAD_REQUEST
- ActionCalledFromServerError
AI-assisted analysis of withastro/astro@d081033d5f (2026-08-12).
Data as JSON: /api/errors/50e7511f6be974f6.
Report an issue: GitHub.