{"record":{"id":"50e7511f6be974f6","repo":"withastro/astro","slug":"astro-actions-defineaction-unexpectedly-used","errorCode":null,"errorMessage":"[astro:actions] `defineAction()` unexpectedly used on the client.","messagePattern":"\\[astro:actions\\] `defineAction\\(\\)` unexpectedly used on the client\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/astro/src/actions/runtime/entrypoints/client.ts","lineNumber":28,"sourceCode":"} from '../client.js';\n\nexport { ACTION_QUERY_PARAMS } from '../../consts.js';\nexport {\n\tActionError,\n\tisActionError,\n\tisInputError,\n} from '../client.js';\nexport type {\n\tActionAPIContext,\n\tActionClient,\n\tActionErrorCode,\n\tActionInputSchema,\n\tActionReturnType,\n\tSafeResult,\n} from '../types.js';\n\nexport function defineAction() {\n\tthrow new Error('[astro:actions] `defineAction()` unexpectedly used on the client.');\n}\n\nexport function getActionContext() {\n\tthrow new Error('[astro:actions] `getActionContext()` unexpectedly used on the client.');\n}\n\nexport const getActionPath = createGetActionPath({\n\tbaseUrl: import.meta.env.BASE_URL,\n\tshouldAppendTrailingSlash,\n});\n\nexport const actions = createActionsProxy({\n\thandleAction: async (param, path) => {\n\t\tconst headers = new Headers();\n\t\theaders.set('Accept', 'application/json');\n\t\t// Apply adapter-specific headers for internal fetches\n\t\tfor (const [key, value] of Object.entries(internalFetchHeaders)) {\n\t\t\theaders.set(key, value);","sourceCodeStart":10,"sourceCodeEnd":46,"githubUrl":"https://github.com/withastro/astro/blob/d081033d5fe8e8a68c4bbbad4af9d2deb9c74bca/packages/astro/src/actions/runtime/entrypoints/client.ts#L10-L46","documentation":"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).","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before - src/actions/index.ts\nimport { defineAction } from 'astro:actions';\nimport { z } from 'astro:schema';\nconst schema = z.object({ email: z.string().email() });\nexport const subscribe = defineAction({ input: schema, handler: async (i) => i });\n// client component imports { schema } from '@/actions' -> pulls defineAction\n\n// after - split schema into src/actions/schemas.ts\nimport { z } from 'astro:schema';\nexport const subscribeSchema = z.object({ email: z.string().email() });\n// client imports { subscribeSchema } from '@/actions/schemas' (no defineAction)","handlingStrategy":"validation","validationCode":"// Build-time guard: fail if a client entrypoint imports a module that calls defineAction.\n// Example using a simple check in a vite plugin or a lint rule over the client entry graph:\nconst CLIENT_FORBIDDEN = /defineAction\\s*\\(/;\n// scan files reachable from src/ that are imported by client.entry.ts\nfor (const file of clientReachableFiles) {\n  if (CLIENT_FORBIDDEN.test(readFile(file))) {\n    throw new Error(`${file} calls defineAction() but is reachable from a client entry`);\n  }\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["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."],"tags":["actions","client-bundle","isomorphic","build"],"backgroundTag":null,"analyzedSha":"d081033d5fe8e8a68c4bbbad4af9d2deb9c74bca","analyzedAt":"2026-08-12T13:37:29.035Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}