komal.nakrani
Browse Themes
ThemesCategoriesDocsBlogTips & Tricks
Server Rendering••By Komal Nakrani

Fix: locals Must Not Be Reassigned in Astro

Astro's 'locals must not be reassigned' means code replaced context.locals with a new object. Set properties on it instead, and type them in src/env.d.ts.

#Astro#Errors#Middleware#Locals

Astro shows “locals must not be reassigned” when middleware or an endpoint does context.locals = { ... }. The message says “locals cannot be assigned directly,” and the hint gives the fix: “Set a locals property instead.” Astro owns the locals object for the whole request; you add to it, you don’t swap it out.

Key Takeaways

  • Write context.locals.user = user, not context.locals = { user }.
  • To set several values, use Object.assign(context.locals, { ... }).
  • Declare the shape once in src/env.d.ts under App.Locals for type safety.
  • Pages read the values from Astro.locals.

Wrong

// src/middleware.ts
import { defineMiddleware } from 'astro:middleware';

export const onRequest = defineMiddleware(async (context, next) => {
  context.locals = { user: await getUser(context.cookies) };
  return next();
});
export const onRequest = defineMiddleware(async (context, next) => {
  context.locals.user = await getUser(context.cookies);
  return next();
});

Several values at once:

Object.assign(context.locals, {
  user: await getUser(context.cookies),
  requestId: crypto.randomUUID(),
});

Type your locals

Without types, context.locals.user is any. Declare it:

// src/env.d.ts
declare namespace App {
  interface Locals {
    user: { id: string; name: string } | null;
    requestId: string;
  }
}

Now pages and endpoints get autocomplete and checks:

---
export const prerender = false;
const { user } = Astro.locals;
---
{user ? <p>Signed in as {user.name}</p> : <a href="/login/">Sign in</a>}

“Value assigned to locals is not accepted” appears when an adapter or integration passes something other than an object as locals. In your own code, the same rule applies: only add properties.

Where locals are available

  • Middleware: context.locals
  • Pages and components: Astro.locals
  • Endpoints and actions: context.locals

On prerendered pages, middleware runs at build time, so locals reflects the build, not a visitor. User-specific values only make sense on on-demand routes; see mixing static and on-demand pages.

Middleware structure is covered in the Astro SSR guide, and the missing-return bug in The middleware didn’t return a Response. The middleware docs document locals.

FAQ

Can I store functions in locals?

Yes. locals lives only on the server for the duration of a request, so it can hold functions, class instances and clients.

Are locals shared between requests?

No. Each request gets a fresh locals object.