withastro/astro · error · AstroError

LocalsReassigned

LocalsReassigned

Error message

`locals` cannot be assigned directly.

What it means

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.

Solutions

  1. Assign properties instead of the object: Astro.locals.user = user
  2. To merge many fields at once, use Object.assign(Astro.locals, values)
  3. Initialize locals fields once in middleware (context.locals.isLogged = ...) so pages never need to reassign
  4. To remove data, delete the key: delete Astro.locals.temp

Example fix

// before
Astro.locals = { user: session.user };

// after
Astro.locals.user = session.user;
Defensive patterns

Strategy: validation

Validate before calling

function setLocals(values: Partial<App.Locals>): void {
  Object.assign(Astro.locals, values);
}
// use setLocals({ user }) instead of Astro.locals = { user }

Prevention

When it happens

Trigger: `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 }.

Common situations: 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.

Related errors


AI-assisted analysis of withastro/astro@e294953aa8 (2026-08-18). Data as JSON: /api/errors/5847eadb40289381. Report an issue: GitHub.

Appendix: source

Thrown at packages/astro/src/core/fetch/fetch-state.ts:1176

		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: getSite(this.manifest),
			url: this.url,
			get originPathname() {
				return getOriginPathname(state.request);
			},
			get csp() {
				return state.getCsp();

View on GitHub (pinned to e294953aa8)