withastro/astro · error · Error

[preview] No adapter found.

Error message

[preview] No adapter found.

What it means

When Astro resolves to server output (`output: 'server'` or any route not prerendered), `astro preview` cannot use the static file server and must delegate to an adapter's preview entrypoint. If no adapter is configured at all, there is no runtime to serve the app, so preview throws immediately after the static branch is skipped.

Solutions

  1. Install and register an adapter: `npx astro add node` (or vercel/netlify/cloudflare as appropriate)
  2. If the site is actually static, set `output: 'static'` (or remove `output: 'server'`) so preview uses the static server
  3. Confirm the astro.config file you edited is the one Astro loads (only one of astro.config.mjs/.ts/.js is used)
  4. Rebuild after adding the adapter: `astro build && astro preview`

Example fix

// before — astro.config.mjs
import { defineConfig } from 'astro/config';
export default defineConfig({ output: 'server' });

// after
import node from '@astrojs/node';
export default defineConfig({ output: 'server', adapter: node({ mode: 'standalone' }) });
Defensive patterns

Strategy: validation

Validate before calling

// scripts/verify-preview.mjs — fail fast if SSR output has no adapter
import config from './astro.config.mjs';
if ((config.output ?? 'static') === 'server' && !config.adapter) {
  console.error('output: \'server\' requires an adapter for `astro preview` — run `npx astro add node`');
  process.exit(1);
}

Prevention

When it happens

Trigger: Setting `output: 'server'` in astro.config without installing an adapter; running `astro preview` on an SSR project where `astro add <adapter>` was never run; defining the adapter in a different config file than the one Astro loads (e.g. a stale astro.config.mjs next to a new astro.config.ts); adapter added conditionally so it is undefined in this environment.

Common situations: Converting a static site to SSR and forgetting the adapter; adapter dependency present in package.json but never registered in the config; monorepo where the adapter is added to the wrong package's config.

Related errors


AI-assisted analysis of withastro/astro@52e6c34790 (2026-08-18). Data as JSON: /api/errors/24a5fe533a1d773f. Report an issue: GitHub.

Appendix: source

Thrown at packages/astro/src/core/preview/index.ts:63

	settings.buildOutput = getPrerenderDefault(settings.config) ? 'static' : 'server';
	await createRoutesList({ settings: settings, cwd: inlineConfig.root }, logger);

	await runHookConfigDone({ settings: settings, logger: logger, command: 'preview' });

	if (settings.buildOutput === 'static' && !settings.adapter?.previewEntrypoint) {
		const clientOutDir = getClientOutputDirectory(settings);
		if (!fs.existsSync(clientOutDir)) {
			const outDirPath = fileURLToPath(clientOutDir);
			throw new Error(
				`[preview] The output directory ${outDirPath} does not exist. Did you run \`astro build\`?`,
			);
		}
		const server = await createStaticPreviewServer(settings, logger);
		return server;
	}

	if (!settings.adapter) {
		throw new Error(`[preview] No adapter found.`);
	}

	if (!settings.adapter.previewEntrypoint) {
		throw new Error(
			`[preview] The ${settings.adapter.name} adapter does not support the preview command.`,
		);
	}
	// We need to use require.resolve() here so that advanced package managers like pnpm
	// don't treat this as a dependency of Astro itself. This correctly resolves the
	// preview entrypoint of the integration package, relative to the user's project root.
	const require = createRequire(settings.config.root);
	const previewEntrypointUrl = pathToFileURL(
		require.resolve(settings.adapter.previewEntrypoint.toString()),
	).href;

	const previewModule = (await import(previewEntrypointUrl)) as Partial<PreviewModule>;
	if (typeof previewModule.default !== 'function') {
		throw new Error(`[preview] ${settings.adapter.name} cannot preview your app.`);

View on GitHub (pinned to 52e6c34790)