withastro/astro · error · AstroError

StaticClientAddressNotAvailable

StaticClientAddressNotAvailable

Error message

`Astro.clientAddress` is only available on pages that are server-rendered.

What it means

createContext() (core middleware context factory, used by adapters such as Vercel and Netlify to bridge edge middleware) only exposes clientAddress when the platform passed one in — the docs on the option say it must come from a trusted source. The getter throws AstroErrorData.StaticClientAddressNotAvailable when no value was supplied, because the standalone context has no other way to know the client IP.

Solutions

  1. Read the IP from platform headers instead: context.request.headers.get('x-forwarded-for') or x-real-ip
  2. Adapter authors: pass the platform-provided trusted address into createContext({ clientAddress })
  3. Use the platform's own geolocation API for edge middleware instead of clientAddress

Example fix

// before
export const onRequest = async (context, next) => {
  rateLimit(context.clientAddress);
  return next();
};

// after
export const onRequest = async (context, next) => {
  const ip = context.request.headers.get('x-forwarded-for')?.split(',')[0]?.trim() ?? 'unknown';
  rateLimit(ip);
  return next();
};
Defensive patterns

Strategy: fallback

Validate before calling

function getEdgeClientIp(request: Request): string | null {
  return (
    request.headers.get('x-forwarded-for')?.split(',')[0]?.trim() ??
    request.headers.get('x-real-ip')
  );
}

Try / catch

let ip: string | null;
try {
  ip = context.clientAddress;
} catch {
  ip = getEdgeClientIp(context.request);
}

Prevention

When it happens

Trigger: Reading context.clientAddress inside middleware that runs against an adapter-created context (Vercel/Netlify edge middleware bridging) where createContext was called without a clientAddress argument; the same middleware code working in the serverless function but throwing at the edge.

Common situations: Geolocation or rate-limiting middleware promoted from functions to edge middleware; adapters that have not wired clientAddress through their edge entry; local tests calling createContext({ request }) without an address.

Related errors


AI-assisted analysis of withastro/astro@e294953aa8 (2026-08-18). Data as JSON: /api/errors/566597465b1c4a04. Report an issue: GitHub.

Appendix: source

Thrown at packages/astro/src/core/middleware/index.ts:131

		isPrerendered: false,
		get preferredLocale(): string | undefined {
			return (preferredLocale ??= computePreferredLocale(request, userDefinedLocales));
		},
		get preferredLocaleList(): string[] | undefined {
			return (preferredLocaleList ??= computePreferredLocaleList(request, userDefinedLocales));
		},
		get currentLocale(): string | undefined {
			return (currentLocale ??= computeCurrentLocale(route, userDefinedLocales, defaultLocale));
		},
		url,
		get originPathname() {
			return getOriginPathname(request);
		},
		get clientAddress() {
			if (clientAddress) {
				return clientAddress;
			}
			throw new AstroError(AstroErrorData.StaticClientAddressNotAvailable);
		},
		get locals() {
			if (typeof locals !== 'object') {
				throw new AstroError(AstroErrorData.LocalsNotAnObject);
			}
			return locals;
		},
		set locals(_) {
			throw new AstroError(AstroErrorData.LocalsReassigned);
		},
		session: undefined,
		cache: new DisabledAstroCache(),
		csp: undefined,
		logger: {
			info() {},
			warn() {},
			error() {},
		},

View on GitHub (pinned to e294953aa8)