withastro/astro · error · Error
Astro.locals.runtime.env has been removed in Astro v6. Use…
Error message
Astro.locals.runtime.env has been removed in Astro v6. Use 'import { env } from "cloudflare:workers"' instead. What it means
Astro v6 removed the `Astro.locals.runtime` compatibility object that older @astrojs/cloudflare versions exposed. The adapter now defines a non-enumerable `runtime` property whose `env` getter throws with migration instructions instead of silently returning data. Hitting it means pre-v6 adapter code is still reading environment bindings from `Astro.locals.runtime.env`.
Solutions
- Replace all reads with the module-level import: `import { env } from 'cloudflare:workers';` then use `env.MY_VAR`.
- Search the codebase for `locals.runtime` and migrate every remaining occurrence (cf, caches, ctx have their own messages).
- Use the official `astro upgrade` flow / upgrade guide which covers adapter migrations.
- If migration is not possible yet, downgrade astro and @astrojs/cloudflare to the last v5-compatible pair.
Example fix
// before
const { DB } = Astro.locals.runtime.env;
// after
import { env } from 'cloudflare:workers';
const { DB } = env; Defensive patterns
Strategy: type-guard
Type guard
// Returns legacy env bindings if present, else undefined — never triggers the trap getter
import { env } from 'cloudflare:workers';
function safeEnv(locals: App.Locals): Record<string, unknown> | undefined {
const runtime = (locals as Record<string, unknown>).runtime;
if (!runtime || typeof runtime !== 'object') return undefined;
try {
return (runtime as { env: unknown }).env as Record<string, unknown>;
} catch {
return undefined; // v6 trap getter threw — use `env` instead
}
} Prevention
- Grep the codebase for `locals.runtime` as part of every major Astro upgrade.
- Adopt `import { env } from 'cloudflare:workers'` at module scope so access is static, not request-time.
- Run the official `astro upgrade` codemods and the adapter migration guide together.
When it happens
Trigger: After upgrading to Astro v6 (or installing the v6-era adapter), any page/middleware/API route executing `Astro.locals.runtime.env.MY_VAR` triggers the getter and throws at request or prerender time.
Common situations: Upgrading an Astro v5 Cloudflare project to v6 without migrating runtime access; copying older snippet/tutorial code that uses locals.runtime.env into a v6 project.
Related errors
- Astro.locals.runtime.caches has been removed in Astro v6…
- Astro.locals.runtime.cf has been removed in Astro v6. Use…
- Astro.locals.runtime.ctx has been removed in Astro v6. Use…
- LegacyContentConfigError
- A content collection is defined with legacy features (e.g…
AI-assisted analysis of withastro/astro@52e6c34790 (2026-08-18).
Data as JSON: /api/errors/5c4085488645bded.
Report an issue: GitHub.
Appendix: source
Thrown at packages/integrations/cloudflare/src/utils/cf-helpers.ts:72
export function createErrorPageFetch(env: Env): (url: string) => Promise<Response> {
return async (url: string) => {
return env.ASSETS.fetch(url.replace(/\.html$/, '')) as unknown as Response;
};
}
/**
* Creates the Cloudflare-specific locals object with `cfContext`
* and deprecated `runtime` property getters.
*/
export function createLocals(ctx: ExecutionContext): Runtime {
const locals: Runtime = {
cfContext: ctx,
};
Object.defineProperty(locals, 'runtime', {
enumerable: false,
value: {
get env(): never {
throw new Error(
`Astro.locals.runtime.env has been removed in Astro v6. Use 'import { env } from "cloudflare:workers"' instead.`,
);
},
get cf(): never {
throw new Error(
`Astro.locals.runtime.cf has been removed in Astro v6. Use 'Astro.request.cf' instead.`,
);
},
get caches(): never {
throw new Error(
`Astro.locals.runtime.caches has been removed in Astro v6. Use the global 'caches' object instead.`,
);
},
get ctx(): never {
throw new Error(
`Astro.locals.runtime.ctx has been removed in Astro v6. Use 'Astro.locals.cfContext' instead.`,
);
},View on GitHub (pinned to 52e6c34790)