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

Thrown by `@astrojs/cloudflare` (v6+) when user code accesses `Astro.locals.runtime.env`. In Astro v6 the `runtime` shim on `locals` is a non-enumerable property whose `env` getter always throws, directing users to import `env` from `cloudflare:workers` instead. This is a hard migration break: the old `runtime.env` object that exposed Cloudflare bindings has been removed.

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 d081033d5f)

Solutions

  1. Replace `Astro.locals.runtime.env.MY_BINDING` with `import { env } from 'cloudflare:workers'; env.MY_BINDING`.
  2. Use a codemod or project-wide search for `locals.runtime.env` and `runtime.env` to find all call sites.
  3. Update TypeScript types if your project extended the old `runtime.env` typing.

Example fix

// before
export const GET = ({ locals }) => Response.json(locals.runtime.env.MY_KV);

// after
import { env } from 'cloudflare:workers';
export const GET = () => Response.json(env.MY_KV);
Defensive patterns

Strategy: validation

Validate before calling

// Codemod guard: fail the build if any v5 runtime.env usage remains.
// In a prebuild script:
const { execSync } = require('child_process');
const out = execSync('grep -rn "runtime.env" src/ || true').toString();
if (out) { console.error('Migrate runtime.env to cloudflare:workers env'); process.exit(1); }

Prevention

When it happens

Trigger: Calling `Astro.locals.runtime.env.MY_BINDING` in a page, endpoint, or middleware running under the Cloudflare adapter on Astro v6. The getter is defined on `locals.runtime` and throws on any property access pattern that touches `env`.

Common situations: Upgrading an Astro v5 (or earlier) Cloudflare project to v6 without migrating binding access. Copy-pasting v5 tutorial code. The `runtime` property is non-enumerable, so it is invisible in `console.log`/`Object.keys`, making the migration trap easy to miss during debugging.

Related errors


AI-assisted analysis of withastro/astro@d081033d5f (2026-08-12). Data as JSON: /api/errors/5c4085488645bded. Report an issue: GitHub.