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

  1. Replace all reads with the module-level import: `import { env } from 'cloudflare:workers';` then use `env.MY_VAR`.
  2. Search the codebase for `locals.runtime` and migrate every remaining occurrence (cf, caches, ctx have their own messages).
  3. Use the official `astro upgrade` flow / upgrade guide which covers adapter migrations.
  4. 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

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


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)