{"record":{"id":"5847eadb40289381","repo":"withastro/astro","slug":"localsreassigned","errorCode":"LocalsReassigned","errorMessage":"`locals` cannot be assigned directly.","messagePattern":"`locals` cannot be assigned directly\\.","errorType":"exception","errorClass":"AstroError","httpStatus":null,"severity":"error","filePath":"packages/astro/src/core/fetch/fetch-state.ts","lineNumber":1176,"sourceCode":"\n\t\tconst ctx = {\n\t\t\tget cookies() {\n\t\t\t\treturn state.cookies;\n\t\t\t},\n\t\t\troutePattern: this.routeData!.route,\n\t\t\tisPrerendered: this.routeData!.prerender,\n\t\t\tget clientAddress() {\n\t\t\t\treturn state.getClientAddress();\n\t\t\t},\n\t\t\tget currentLocale() {\n\t\t\t\treturn state.computeCurrentLocale();\n\t\t\t},\n\t\t\tgenerator: ASTRO_GENERATOR,\n\t\t\tget locals() {\n\t\t\t\treturn state.locals;\n\t\t\t},\n\t\t\tset locals(_) {\n\t\t\t\tthrow new AstroError(AstroErrorData.LocalsReassigned);\n\t\t\t},\n\t\t\t// SAFETY: getActionAPIContext is only called after route resolution,\n\t\t\t// so routeData is always set and the params getter always returns a value.\n\t\t\tparams: this.params!,\n\t\t\tget preferredLocale() {\n\t\t\t\treturn state.computePreferredLocale();\n\t\t\t},\n\t\t\tget preferredLocaleList() {\n\t\t\t\treturn state.computePreferredLocaleList();\n\t\t\t},\n\t\t\trequest: this.request,\n\t\t\tsite: getSite(this.manifest),\n\t\t\turl: this.url,\n\t\t\tget originPathname() {\n\t\t\t\treturn getOriginPathname(state.request);\n\t\t\t},\n\t\t\tget csp() {\n\t\t\t\treturn state.getCsp();","sourceCodeStart":1158,"sourceCodeEnd":1194,"githubUrl":"https://github.com/withastro/astro/blob/e294953aa8aadd98d5be92e60a03037b05dbdfd4/packages/astro/src/core/fetch/fetch-state.ts#L1158-L1194","documentation":"On the fetch-state-backed Astro global, `locals` is getter-only; the setter throws AstroErrorData.LocalsReassigned. The locals object keeps one stable identity for the whole request so middleware, pages, and actions can decorate the same object — replacing the instance would orphan every reference already handed out. Mutating properties is always allowed.","triggerScenarios":"`Astro.locals = {...}` anywhere in .astro frontmatter, middleware, endpoints, or actions; attempting to 'reset' locals by assigning a fresh object; spread-style replacement like Astro.locals = { ...Astro.locals, user }.","commonSituations":"Developers used to frameworks where ctx.state is assignable; refactors that move locals initialization from middleware into a page; trying to clear stale keys between requests in dev and writing an assignment.","solutions":["Assign properties instead of the object: Astro.locals.user = user","To merge many fields at once, use Object.assign(Astro.locals, values)","Initialize locals fields once in middleware (context.locals.isLogged = ...) so pages never need to reassign","To remove data, delete the key: delete Astro.locals.temp"],"exampleFix":"// before\nAstro.locals = { user: session.user };\n\n// after\nAstro.locals.user = session.user;","handlingStrategy":"validation","validationCode":"function setLocals(values: Partial<App.Locals>): void {\n  Object.assign(Astro.locals, values);\n}\n// use setLocals({ user }) instead of Astro.locals = { user }","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Initialize locals fields in middleware (context.locals.x = ...) once per request","Only mutate properties; never assign the container","Grep for 'locals =' (with the assignment operator) during code review"],"tags":["locals","astro-global","readonly-property","assignment"],"backgroundTag":"readonly-property-reassignment","analyzedSha":"e294953aa8aadd98d5be92e60a03037b05dbdfd4","analyzedAt":"2026-08-18T18:48:03.901Z","contentChangedAt":"2026-08-18T18:48:03.901Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}