withastro/astro · error · Error

You tried to add the ${name} client renderer, but its server

Error message

You tried to add the ${name} client renderer, but its server renderer wasn't added. You must add the server renderer first. Use the `addServerRenderer` function.

What it means

Client renderers hydrate `client:*` components in the browser, but they must be paired with a server renderer already present in the manifest. `addClientRenderer` looks up the renderer by `name` and attaches its `clientEntrypoint`; if no server renderer with that name exists, the lookup returns `-1` and the call is rejected.

Source

Thrown at packages/astro/src/container/index.ts:425

	 *
	 * const container = await AstroContainer.create();
	 * container.addServerRenderer(reactRenderer);
	 * container.addClientRenderer({
	 * 	name: "@astrojs/react",
	 * 	entrypoint: "@astrojs/react/client.js"
	 * });
	 * ```
	 *
	 * @param options {object}
	 * @param options.name The name of the renderer. The name **isn't** arbitrary, and it should match the name of the package.
	 * @param options.entrypoint The entrypoint of the client renderer.
	 */
	public addClientRenderer(options: AddClientRenderer): void {
		const { entrypoint, name } = options;

		const rendererIndex = this.#pipeline.manifest.renderers.findIndex((r) => r.name === name);
		if (rendererIndex === -1) {
			throw new Error(
				'You tried to add the ' +
					name +
					" client renderer, but its server renderer wasn't added. You must add the server renderer first. Use the `addServerRenderer` function.",
			);
		}
		const renderer = this.#pipeline.manifest.renderers[rendererIndex];
		renderer.clientEntrypoint = entrypoint;

		this.#pipeline.manifest.renderers[rendererIndex] = renderer;
	}

	// NOTE: we keep this private via TS instead via `#` so it's still available on the surface, so we can play with it.
	// @ts-expect-error @ematipico: I plan to use it for a possible integration that could help people
	private static async createFromManifest(
		manifest: SSRManifest,
	): Promise<experimental_AstroContainer> {
		const container = new experimental_AstroContainer({
			manifest,

View on GitHub (pinned to d081033d5f)

Solutions

  1. Call `addServerRenderer` with the same `name` before `addClientRenderer`.
  2. Verify the `name` strings match exactly (usually the integration package name, e.g. `@astrojs/react`).
  3. If rendering server-only components, you can skip `addClientRenderer` entirely.

Example fix

// before
container.addClientRenderer({ name: '@astrojs/react', entrypoint: '@astrojs/react/client.js' });
// after
container.addServerRenderer(reactRenderer);
container.addClientRenderer({ name: '@astrojs/react', entrypoint: '@astrojs/react/client.js' });
Defensive patterns

Strategy: validation

Validate before calling

const has = container['#pipeline'].manifest.renderers.some(r => r.name === name);
if (!has) throw new Error(`Add server renderer ${name} first`);

Type guard

function serverRendererRegistered(manifest, name) {
  return manifest.renderers.some(r => r.name === name);
}

Prevention

When it happens

Trigger: Calling `container.addClientRenderer({ name, entrypoint })` before the matching `container.addServerRenderer(...)` for the same `name`.

Common situations: Reversing the documented add-server-then-add-client order; name mismatch (e.g. server registered as `'@astrojs/react'` but client passed as `'react'`); forgetting to call `addServerRenderer` at all when using `AstroContainer.create()` from a minimal manifest.

Related errors


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