{"record":{"id":"14f3ec11b7f78149","repo":"withastro/astro","slug":"astro-actions-getactioncontext-unexpectedly","errorCode":null,"errorMessage":"[astro:actions] `getActionContext()` unexpectedly used on the client.","messagePattern":"\\[astro:actions\\] `getActionContext\\(\\)` unexpectedly used on the client\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/astro/src/actions/runtime/entrypoints/client.ts","lineNumber":32,"sourceCode":"\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);\n\t\t}\n\t\tlet body = param;\n\t\tif (!(body instanceof FormData)) {\n\t\t\ttry {","sourceCodeStart":14,"sourceCodeEnd":50,"githubUrl":"https://github.com/withastro/astro/blob/d081033d5fe8e8a68c4bbbad4af9d2deb9c74bca/packages/astro/src/actions/runtime/entrypoints/client.ts#L14-L50","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Keep getActionContext()-using code in server-only modules and never import those modules from the client.","Split shared helpers so server-context code is isolated from client-importable code.","Use import type on the client to avoid pulling runtime action internals."],"exampleFix":"// before - src/lib/auth.ts used by both handler and client\nimport { getActionContext } from 'astro:actions';\nexport function currentUserId() {\n  const ctx = getActionContext();\n  return ctx.locals.user?.id;\n}\n// after - src/lib/auth.server.ts (server only)\nexport function currentUserId() {\n  const ctx = getActionContext();\n  return ctx.locals.user?.id;\n}\n// client imports from a separate client-safe module instead","handlingStrategy":"validation","validationCode":"// Fail the build if a client-reachable module references getActionContext.\nconst CLIENT_FORBIDDEN = /getActionContext/;\nfor (const file of clientReachableFiles) {\n  if (CLIENT_FORBIDDEN.test(readFile(file))) {\n    throw new Error(`${file} uses server-only getActionContext() but is client-reachable`);\n  }\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Isolate getActionContext()-using helpers in *.server.ts files.","Never import action-handler internals into client components.","Use import type for cross-boundary type sharing."],"tags":["actions","client-bundle","isomorphic","build"],"backgroundTag":null,"analyzedSha":"d081033d5fe8e8a68c4bbbad4af9d2deb9c74bca","analyzedAt":"2026-08-12T13:37:29.035Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}