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, notcontext.locals = { user }.- To set several values, use
Object.assign(context.locals, { ... }).- Declare the shape once in
src/env.d.tsunderApp.Localsfor 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();
});
Right
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>}
The related error
“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.