withastro/astro · error · AstroError
LocalsReassigned
LocalsReassigned
Error message
`locals` cannot be assigned directly.
What it means
Thrown by the setter on the Astro global's `locals` property. Astro.locals is meant to be mutated in place (Astro.locals.user = x), not reassigned wholesale, because the context holds a stable reference shared across middleware and the page. Reassigning would break that shared reference.
Source
Thrown at packages/astro/src/core/fetch/fetch-state.ts:1127
const ctx = {
get cookies() {
return state.cookies;
},
routePattern: this.routeData!.route,
isPrerendered: this.routeData!.prerender,
get clientAddress() {
return state.getClientAddress();
},
get currentLocale() {
return state.computeCurrentLocale();
},
generator: ASTRO_GENERATOR,
get locals() {
return state.locals;
},
set locals(_) {
throw new AstroError(AstroErrorData.LocalsReassigned);
},
// SAFETY: getActionAPIContext is only called after route resolution,
// so routeData is always set and the params getter always returns a value.
params: this.params!,
get preferredLocale() {
return state.computePreferredLocale();
},
get preferredLocaleList() {
return state.computePreferredLocaleList();
},
request: this.request,
site: this.pipeline.site,
url: this.url,
get originPathname() {
return getOriginPathname(state.request);
},
get csp() {
return state.getCsp();View on GitHub (pinned to d081033d5f)
Solutions
- Mutate instead of reassign: set individual properties, e.g. `Astro.locals.user = user`.
- If you need to reset, delete keys individually or assign to existing properties rather than replacing the object.
- Initialize locals shape in middleware via context.locals.x = value, never context.locals = {...}.
Example fix
// before
Astro.locals = { user };
// after
Astro.locals.user = user; Defensive patterns
Strategy: validation
Validate before calling
// Never reassign; always mutate properties.
Astro.locals.user = user; // ok
Astro.locals = { user }; // forbidden Type guard
// locals must be an object you mutate, not replace.
function assertLocalsMutable(locals: unknown): asserts locals is Record<string, unknown> {
if (typeof locals !== 'object' || locals === null) {
throw new Error('locals must be an object');
}
} Try / catch
try {
Astro.locals.x = 1;
} catch (e) {
if (e instanceof Error && /cannot be assigned directly/i.test(e.message)) {
// fix the reassignment; this catch indicates the setter was hit
}
throw e;
} Prevention
- Always use property assignment on locals, never whole-object assignment.
- Configure a linter/ESLint rule to flag `locals =`.
- Document the mutation-only contract in your project's conventions.
When it happens
Trigger: Writing `Astro.locals = { ... }` (assignment to the property itself) inside a page, layout, endpoint, or action. The setter defined in createAstroPagePartial throws AstroErrorData.LocalsReassigned unconditionally.
Common situations: Developers used to initializing locals fresh per request who write `Astro.locals = {}`; refactoring middleware that does `context.locals = something` (the same guard exists on the middleware context); copying example code that reassigns locals.
Related errors
- LocalsNotAnObject
- LocalsReassigned
- The passed value can't be serialized.
- MiddlewareNotAResponse
- MiddlewareNoDataOrNextCalled
AI-assisted analysis of withastro/astro@d081033d5f (2026-08-12).
Data as JSON: /api/errors/5847eadb40289381.
Report an issue: GitHub.