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

  1. In browser code, import only the callable proxy from 'astro:actions' — never relatively import src/actions/index.ts
  2. Move shared zod schemas and helpers into a separate schema.ts module that both the actions file and client components import
  3. 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

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


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)