withastro/astro · error · AstroError

ServerOnlyModule

ServerOnlyModule

Error message

The "astro:config/server" module is only available server-side.

What it means

The `astro:config/server` virtual module is the server-only half of the serialized config. When its `resolveId`/`load` handler runs under the Astro client environment (`environment.name === ASTRO_VITE_ENVIRONMENT_NAMES.client`), Astro throws `ServerOnlyModule` to keep server-only config (and any server virtual modules it pulls in) out of client bundles.

Source

Thrown at packages/astro/src/manifest/virtual-module.ts:88

					return RESOLVED_VIRTUAL_CLIENT_ID;
				}
			},
		},
		load: {
			filter: {
				id: new RegExp(`^(${RESOLVED_VIRTUAL_SERVER_ID}|${RESOLVED_VIRTUAL_CLIENT_ID})$`),
			},
			handler(id) {
				if (id === RESOLVED_VIRTUAL_CLIENT_ID) {
					// astro:config/client inlines values directly from settings instead of
					// importing from virtual:astro:manifest to avoid pulling server-only
					// virtual modules (virtual:astro:routes, virtual:astro:pages) into the
					// client environment where they are not available.
					return { code: clientConfigCode };
				}
				if (id === RESOLVED_VIRTUAL_SERVER_ID) {
					if (this.environment.name === ASTRO_VITE_ENVIRONMENT_NAMES.client) {
						throw new AstroError({
							...AstroErrorData.ServerOnlyModule,
							message: AstroErrorData.ServerOnlyModule.message(VIRTUAL_SERVER_ID),
						});
					}
					const code = `
import { manifest } from '${SERIALIZED_MANIFEST_ID}'
import { fromRoutingStrategy } from "astro/app";

let i18n = undefined;
if (manifest.i18n) {
 i18n = {
   defaultLocale: manifest.i18n.defaultLocale,
   locales: manifest.i18n.locales,
   routing: fromRoutingStrategy(manifest.i18n.strategy, manifest.i18n.fallbackType),
   fallback: manifest.i18n.fallback,
   domains: manifest.i18n.domains,
 };
}

View on GitHub (pinned to d081033d5f)

Solutions

  1. Remove the `astro:config/server` import from any client-reachable module; use `astro:config/client` for client-safe config values.
  2. Split server-only config consumers into separate files the browser never imports.
  3. Trace the import graph to find the client entrypoint pulling in the server module and cut the edge.

Example fix

// before — shared util imported on both sides
import { config } from 'astro:config/server'
export function getBase() { return config.base }

// after — client code uses the client-safe module
import { config } from 'astro:config/client'
export function getBase() { return config.base }
Defensive patterns

Strategy: validation

Validate before calling

// Static check: ensure no client-reachable file imports astro:config/server.
// Use a grep/ESLint rule disallowing 'astro:config/server' outside server-only dirs.

Type guard

null

Try / catch

null

Prevention

When it happens

Trigger: Importing `astro:config/server` (directly or transitively) from code that resolves in the client environment — client components, `<script>` tags, client directive entrypoints.

Common situations: A shared utility that imports server config being pulled into a client-rendered component; a barrel file re-exporting `astro:config/server`; mixing server and client config imports in one module graph.

Related errors


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