withastro/astro · error · AstroError

ActionNotFoundError

ActionNotFoundError

Error message

The server received a request for an action named `${actionName}` but could not find a match. If you renamed an action, check that you've updated your `actions/index` file and your calling code to match.

What it means

An `ActionNotFoundError` thrown during action path traversal when an intermediate path segment resolves to a function before all keys are consumed. Actions are leaf nodes — once the traversal reaches a function, it cannot go deeper. For example, requesting `foo.bar` when `foo` is already an action function triggers this. The guard is `if (typeof server === 'function')` inside the key loop.

Source

Thrown at packages/astro/src/core/base-pipeline.ts:380

			serverIslandNameMap: new Map(),
		};
	}

	async getAction(path: string): Promise<ActionClient<unknown, ActionAccept, $ZodType>> {
		const pathKeys = path.split('.').map((key) => decodeURIComponent(key));
		let { server } = await this.getActions();

		if (!server || !(typeof server === 'object')) {
			throw new TypeError(
				`Expected \`server\` export in actions file to be an object. Received ${typeof server}.`,
			);
		}

		for (const key of pathKeys) {
			// An action is a leaf: once resolved to a function, its own properties
			// are not part of the action namespace and cannot be traversed further.
			if (typeof server === 'function') {
				throw new AstroError({
					...ActionNotFoundError,
					message: ActionNotFoundError.message(pathKeys.join('.')),
				});
			}
			if (FORBIDDEN_PATH_KEYS.has(key)) {
				throw new AstroError({
					...ActionNotFoundError,
					message: ActionNotFoundError.message(pathKeys.join('.')),
				});
			}
			if (!Object.hasOwn(server, key)) {
				throw new AstroError({
					...ActionNotFoundError,
					message: ActionNotFoundError.message(pathKeys.join('.')),
				});
			}
			// @ts-expect-error we are doing a recursion... it's ugly
			server = server[key];

View on GitHub (pinned to d081033d5f)

Solutions

  1. Check that the action path doesn't traverse through an existing leaf action name.
  2. Rename the conflicting action or namespace so they don't overlap.
  3. Run `astro check` to detect mismatched action names.
  4. Ensure `Astro.callAction` uses the correct dotted path matching your `server` export structure.

Example fix

// before — src/actions/index.ts
export const server = {
  login: defineAction({ handler: async () => { ... } }), // leaf
};
// calling Astro.callAction('login.google') → error

// after
export const server = {
  login: {
    password: defineAction({ handler: async () => { ... } }),
    google: defineAction({ handler: async () => { ... } }),
  },
};
Defensive patterns

Strategy: validation

Validate before calling

function isValidActionPath(server: object, path: string): boolean {
  let current: unknown = server;
  for (const key of path.split('.')) {
    if (typeof current === 'function') return false; // hit a leaf too early
    if (!Object.hasOwn(current as object, key)) return false;
    current = (current as Record<string, unknown>)[key];
  }
  return typeof current === 'function';
}

Try / catch

try {
  await Astro.callAction('users.delete', input);
} catch (e) {
  if (e instanceof Error && e.name === 'ActionNotFoundError') {
    console.error('Action path invalid:', e.message);
  }
  throw e;
}

Prevention

When it happens

Trigger: `getAction('parent.child')` is called. During traversal, `server` is reassigned to `server['parent']` which is a function. On the next iteration for `'child'`, the `typeof server === 'function'` check fires and `ActionNotFoundError` is thrown with the full dotted path.

Common situations: Calling a nested action path where the parent is a flat action (e.g. `Astro.callAction('login.google')` when `login` is itself a function). A client sends a fabricated action path. Renaming/refactoring actions creates a naming collision where a namespace name is also an action name.

Related errors


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