What Causes This Error?
This error occurs when you try to assign a non-object value to context.locals or Astro.locals. Locals must be an object that you can add properties to.
The Problem
// src/middleware.ts
export const onRequest = defineMiddleware((context, next) => {
// ❌ Assigning string
context.locals = 'user data';
// ❌ Assigning array
context.locals = ['item1', 'item2'];
// ❌ Assigning null
context.locals = null;
return next();
});
The Fix
Assign Properties to Locals Object
// src/middleware.ts
import { defineMiddleware } from 'astro:middleware';
export const onRequest = defineMiddleware((context, next) => {
// ✅ Assign properties to the existing object
context.locals.user = 'John';
context.locals.isAuthenticated = true;
context.locals.data = ['item1', 'item2'];
return next();
});
Common Scenarios
Setting User Data
// ❌ Wrong - replacing locals
context.locals = { user: userData };
// ✅ Correct - adding to locals
context.locals.user = userData;
Multiple Properties
export const onRequest = defineMiddleware(async (context, next) => {
const session = await getSession(context.cookies);
// ✅ Add multiple properties
context.locals.user = session?.user ?? null;
context.locals.isAuthenticated = !!session;
context.locals.permissions = session?.permissions ?? [];
return next();
});
In Pages
---
// ✅ Access locals in pages
const { user, isAuthenticated } = Astro.locals;
---
{isAuthenticated ? (
<p>Welcome, {user.name}</p>
) : (
<p>Please log in</p>
)}
TypeScript Typing
// src/env.d.ts
declare namespace App {
interface Locals {
user: {
id: string;
name: string;
} | null;
isAuthenticated: boolean;
}
}
// src/middleware.ts
export const onRequest = defineMiddleware((context, next) => {
// Now TypeScript knows the shape
context.locals.user = { id: '1', name: 'John' };
context.locals.isAuthenticated = true;
return next();
});
Conditional Assignment
export const onRequest = defineMiddleware(async (context, next) => {
// ✅ Conditionally add properties
if (context.url.pathname.startsWith('/admin')) {
context.locals.adminMode = true;
}
const token = context.cookies.get('token');
if (token) {
context.locals.token = token.value;
}
return next();
});
Object Spread Won’t Work
// ❌ Can't spread into locals
const userData = { user: 'John', role: 'admin' };
context.locals = { ...context.locals, ...userData };
// ✅ Use Object.assign or individual properties
Object.assign(context.locals, userData);
// or
context.locals.user = userData.user;
context.locals.role = userData.role;
In API Endpoints
// src/pages/api/user.ts
import type { APIRoute } from 'astro';
export const GET: APIRoute = ({ locals }) => {
// Access locals set by middleware
const { user, isAuthenticated } = locals;
if (!isAuthenticated) {
return new Response('Unauthorized', { status: 401 });
}
return Response.json(user);
};
Default Values
export const onRequest = defineMiddleware((context, next) => {
// ✅ Set defaults
context.locals.theme = context.locals.theme ?? 'light';
context.locals.language = context.locals.language ?? 'en';
return next();
});
Complex Objects
export const onRequest = defineMiddleware((context, next) => {
// ✅ Complex nested objects are fine
context.locals.config = {
api: {
baseUrl: 'https://api.example.com',
timeout: 5000,
},
features: {
darkMode: true,
notifications: false,
},
};
return next();
});
Quick Checklist
- Don’t replace
locals- add properties to it - Use
context.locals.property = value - Type locals in
src/env.d.ts - Access in pages via
Astro.locals - Use
Object.assign()for bulk properties