withastro/astro · error · TypeError
Expected `server` export in actions file to be an object…
Error message
Expected `server` export in actions file to be an object. Received ${typeof server}. What it means
When resolving an action, Astro loads the server-side actions module from the manifest and expects its `server` export to be a plain object that forms the action namespace. If that export is missing, falsy, or not an object (a function, string, etc.), action lookup cannot proceed and this TypeError is thrown at load time.
Solutions
- Open src/actions/index.ts and verify it exports actions correctly — either named `defineAction()` exports or an object under `server` — matching your Astro version's docs
- Restart the dev server (or clear node_modules/.vite and rebuild) so the astro:actions virtual module regenerates
- If you import astro:actions/server.js directly somewhere, stop — use the documented `astro:actions` entry so the bundler resolves the right module
- Check for a build error earlier in the log: a failed actions-file compile can surface here as a bad server export
Example fix
// before — src/actions/index.ts
export const server = () => defineAction({ /* ... */ }); // function, not an object
// after
import { defineAction } from 'astro:actions';
export const server = {
getGame: defineAction({
input: z.object({ id: z.string() }),
handler: (input) => ({ id: input.id }),
}),
}; Defensive patterns
Strategy: validation
Validate before calling
// in dev-only bootstrap code, assert the shape before first use
import * as actionsModule from './actions/index.js';
const server = (actionsModule as any).server ?? actionsModule;
if (typeof server !== 'object' || server === null) {
throw new Error('actions/index.ts must export the actions namespace as an object');
} Type guard
function isValidActionsServer(v: unknown): v is Record<string, unknown> {
return typeof v === 'object' && v !== null && !Array.isArray(v);
} Try / catch
try {
const action = await getAction(manifest, 'blog.like');
} catch (e) {
if (e instanceof TypeError && e.message.includes('`server` export')) {
// fix actions/index.ts exports, then restart dev server
} else throw e;
} Prevention
- Keep src/actions/index.ts minimal: only defineAction exports and nested namespaces
- Run astro check / typecheck so a malformed actions file fails in CI, not at runtime
- Restart the dev server after restructuring the actions file so the virtual module regenerates
When it happens
Trigger: src/actions/index.ts exporting `server` as a non-object (e.g. `export const server = someFunction`), exporting a default instead of the expected shape, or the built/virtual astro:actions module resolving to garbage after a broken build or interrupted HMR update. Any getAction() call — RPC POST or server-side callAction — hits this first.
Common situations: Upgrading between Astro versions where the expected actions-file export shape changed (server object vs named exports); hand-editing the actions index after scaffolding; a stale dist/ or node_modules/.vite cache serving a malformed virtual module.
Related errors
- Expected handler for action
- Unsupported content type
- ActionCalledFromServerError
- ActionCalledFromServerError
- ActionNotFoundError
AI-assisted analysis of withastro/astro@52e6c34790 (2026-08-18).
Data as JSON: /api/errors/8feb0faab1e253b1.
Report an issue: GitHub.
Appendix: source
Thrown at packages/astro/src/actions/load.ts:36
/**
* Clears the cached actions so they are re-resolved on the next request.
* Called via HMR when action files change during development.
*/
export function clearActions(manifest: SSRManifest): void {
actionsMemo.invalidate(manifest);
}
/** Looks up a single action handler by its dot-separated path. */
export async function getAction(
manifest: SSRManifest,
path: string,
): Promise<ActionClient<unknown, ActionAccept, $ZodType>> {
const pathKeys = path.split('.').map((key) => decodeURIComponent(key));
let { server } = await getActions(manifest);
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('.')),
});View on GitHub (pinned to 52e6c34790)