withastro/astro · error · AstroError

StaticClientAddressNotAvailable

StaticClientAddressNotAvailable

Error message

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

What it means

Thrown by the clientAddress getter on the APIContext returned from createContext (used in dev/middleware contexts). When no clientAddress was passed to the context, reading it is meaningless, so StaticClientAddressNotAvailable is thrown. Unlike the route-aware getter, this context has no prerender/adapter logic, only a presence check.

Source

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

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

Solutions

  1. Only read clientAddress in contexts where it is provided (server-rendered requests with a supporting adapter).
  2. Guard with an adapter check or feature flag before reading clientAddress in middleware.
  3. Fall back to request headers when clientAddress is unavailable.

Example fix

// before
export const onRequest = (ctx, next) => {
  log(ctx.clientAddress);
  return next();
};

// after
export const onRequest = (ctx, next) => {
  log(ctx.clientAddress ?? ctx.request.headers.get('x-forwarded-for'));
  return next();
};
Defensive patterns

Strategy: validation

Validate before calling

// In middleware, guard clientAddress access.
export const onRequest = (ctx, next) => {
  const ip = ctx.clientAddress ?? ctx.request.headers.get('x-forwarded-for');
  return next();
};

Type guard

function hasClientAddress(ctx: { clientAddress?: string }): boolean {
  return typeof ctx.clientAddress === 'string' && ctx.clientAddress.length > 0;
}

Try / catch

try {
  ip = ctx.clientAddress;
} catch (e) {
  if (e instanceof Error && /server-rendered/i.test(e.message)) {
    ip = ctx.request.headers.get('x-forwarded-for') ?? undefined;
  } else throw e;
}

Prevention

When it happens

Trigger: Reading context.clientAddress from middleware or an APIContext that was created without a clientAddress argument (e.g. during dev, tests, or an SSG build). The getter throws when the local clientAddress variable is falsy.

Common situations: Accessing clientAddress inside middleware that also runs during prerender/dev when no address exists; SSR middleware in a static-output project; reading clientAddress before an adapter is configured.

Related errors


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