withastro/astro · error · Error
Astro.locals.runtime.ctx has been removed in Astro v6. Use…
Error message
Astro.locals.runtime.ctx has been removed in Astro v6. Use 'Astro.locals.cfContext' instead.
What it means
Last of the Astro v6 removals: `Astro.locals.runtime.ctx` (the ExecutionContext with waitUntil, passThroughOnException, caches accessor) is gone. The trap getter throws and points to the replacement, `Astro.locals.cfContext`, which the v6 adapter populates with the same ExecutionContext.
Solutions
- Replace `Astro.locals.runtime.ctx` with `Astro.locals.cfContext`.
- Update the App.Locals type to declare `cfContext: ExecutionContext` for type safety.
- Sweep for the other removed properties (env, cf, caches) at the same time.
- Remain on Astro v5 with the matching adapter if migration must wait.
Example fix
// before Astro.locals.runtime.ctx.waitUntil(sendAnalytics()); // after Astro.locals.cfContext.waitUntil(sendAnalytics());
Defensive patterns
Strategy: type-guard
Type guard
interface CfLocals extends App.Locals {
cfContext?: ExecutionContext;
}
function safeExecContext(locals: App.Locals): ExecutionContext | undefined {
const v6 = (locals as CfLocals).cfContext;
if (v6) return v6;
const runtime = (locals as Record<string, unknown>).runtime;
if (!runtime || typeof runtime !== 'object') return undefined;
try {
return (runtime as { ctx: unknown }).ctx as ExecutionContext;
} catch {
return undefined; // v6 trap — migrate to Astro.locals.cfContext
}
} Prevention
- Extend App.Locals to declare `cfContext: ExecutionContext` so TS flags old `runtime.ctx` reads.
- Centralize waitUntil usage in one helper instead of scattering locals access across pages.
- Migrate all four removed properties (env/cf/caches/ctx) in a single upgrade pass to avoid repeat breakage.
When it happens
Trigger: Calling `Astro.locals.runtime.ctx.waitUntil(...)` (analytics beacons, deferred cache fills) in an Astro v6 project throws the moment the getter executes.
Common situations: Deferred-work patterns from Astro v5 projects; code copied from older Cloudflare adapter docs that used locals.runtime.ctx.
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.env 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/94eee2c8c2c52a18.
Report an issue: GitHub.
Appendix: source
Thrown at packages/integrations/cloudflare/src/utils/cf-helpers.ts:87
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.`,
);
},
},
});
return locals;
}
/**
* Extracts the client IP address from the `cf-connecting-ip` header.
*/
export function getClientAddress(request: Request): string | undefined {
return getValidatedIpFromHeader(request.headers.get('cf-connecting-ip'));
}
View on GitHub (pinned to 52e6c34790)