{"record":{"id":"0b37ba74e79d13c2","repo":"withastro/astro","slug":"localsreassigned-0b37ba","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/middleware/index.ts","lineNumber":140,"sourceCode":"\t\t},\n\t\turl,\n\t\tget originPathname() {\n\t\t\treturn getOriginPathname(request);\n\t\t},\n\t\tget clientAddress() {\n\t\t\tif (clientAddress) {\n\t\t\t\treturn clientAddress;\n\t\t\t}\n\t\t\tthrow new AstroError(AstroErrorData.StaticClientAddressNotAvailable);\n\t\t},\n\t\tget locals() {\n\t\t\tif (typeof locals !== 'object') {\n\t\t\t\tthrow new AstroError(AstroErrorData.LocalsNotAnObject);\n\t\t\t}\n\t\t\treturn locals;\n\t\t},\n\t\tset locals(_) {\n\t\t\tthrow new AstroError(AstroErrorData.LocalsReassigned);\n\t\t},\n\t\tsession: undefined,\n\t\tcache: new DisabledAstroCache(),\n\t\tcsp: undefined,\n\t\tlogger: {\n\t\t\tinfo() {},\n\t\t\twarn() {},\n\t\t\terror() {},\n\t\t},\n\t};\n\treturn Object.assign(context, {\n\t\tgetActionResult: createGetActionResult(context.locals),\n\t\tcallAction: createCallAction(context),\n\t});\n}\n\n/**\n * Checks whether the passed `value` is serializable.","sourceCodeStart":122,"sourceCodeEnd":158,"githubUrl":"https://github.com/withastro/astro/blob/e294953aa8aadd98d5be92e60a03037b05dbdfd4/packages/astro/src/core/middleware/index.ts#L122-L158","documentation":"The setter for locals on createContext()-built contexts always throws AstroErrorData.LocalsReassigned. As with Astro.locals, the object identity is shared with everything downstream of the context, so it can only be decorated property-by-property, never replaced. This is the same contract enforced on every Astro context variant.","triggerScenarios":"`context.locals = {...}` inside middleware running against an adapter-created (e.g. edge-bridged) context; attempting to reassign locals to a fresh object after parsing them off a header.","commonSituations":"Edge middleware that receives locals from a header and 'restores' them by assignment instead of merging; developers porting code where ctx.state is assignable; initializing locals at the top of middleware with an assignment.","solutions":["Mutate instead: context.locals.user = user","To restore many fields: Object.assign(context.locals, parsedLocals)","Initialize individual fields early in middleware rather than replacing the container"],"exampleFix":"// before\nconst parsed = JSON.parse(request.headers.get('x-astro-locals') ?? '{}');\ncontext.locals = parsed;\n\n// after\nconst parsed = JSON.parse(request.headers.get('x-astro-locals') ?? '{}');\nObject.assign(context.locals, parsed);","handlingStrategy":"validation","validationCode":"function restoreLocals(context: APIContext, parsed: Record<string, unknown>): void {\n  Object.assign(context.locals, parsed);\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Mutate context.locals properties; never assign the object","When restoring forwarded locals, Object.assign into the existing container","Initialize fields one by one at the top of middleware instead of replacing the container"],"tags":["locals","readonly-property","middleware","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"}