withastro/astro · error · Error

[astro:actions] `getActionContext()` unexpectedly used on th

Error message

[astro:actions] `getActionContext()` unexpectedly used on the client.

What it means

The client stub for getActionContext() throws on any invocation in the browser. getActionContext() is a server-only API used inside action handlers to read the request context; like defineAction() it must never execute client-side. Hitting it means server action runtime code was imported into the client.

Source

Thrown at packages/astro/src/actions/runtime/entrypoints/client.ts:32

	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);
		}
		let body = param;
		if (!(body instanceof FormData)) {
			try {

View on GitHub (pinned to d081033d5f)

Solutions

  1. Keep getActionContext()-using code in server-only modules and never import those modules from the client.
  2. Split shared helpers so server-context code is isolated from client-importable code.
  3. Use import type on the client to avoid pulling runtime action internals.

Example fix

// before - src/lib/auth.ts used by both handler and client
import { getActionContext } from 'astro:actions';
export function currentUserId() {
  const ctx = getActionContext();
  return ctx.locals.user?.id;
}
// after - src/lib/auth.server.ts (server only)
export function currentUserId() {
  const ctx = getActionContext();
  return ctx.locals.user?.id;
}
// client imports from a separate client-safe module instead
Defensive patterns

Strategy: validation

Validate before calling

// Fail the build if a client-reachable module references getActionContext.
const CLIENT_FORBIDDEN = /getActionContext/;
for (const file of clientReachableFiles) {
  if (CLIENT_FORBIDDEN.test(readFile(file))) {
    throw new Error(`${file} uses server-only getActionContext() but is client-reachable`);
  }
}

Prevention

When it happens

Trigger: Importing a module that calls getActionContext() (typically an action handler utility) into client code; sharing handler-side helpers between server and client without splitting them.

Common situations: A shared utils file used by both an action handler (calls getActionContext) and a client component; re-exporting handler internals through a barrel that the client imports.

Related errors


AI-assisted analysis of withastro/astro@d081033d5f (2026-08-12). Data as JSON: /api/errors/14f3ec11b7f78149. Report an issue: GitHub.