withastro/astro · error · Error

Another astro dev server is already running. URL

Error message

Another astro dev server is already running.

  URL:  ${existingServer.url}
  PID:  ${existingServer.pid}

Run `astro dev stop` to stop it, or use `astro dev --force` to replace it.

What it means

Before starting, `astro dev` reads the project's lock file (`.astro/dev.json` under the project root) and checks the recorded PID. A live dev server is already registered for this root; starting a second one would make `astro dev stop`/`status`/`logs` unreliable, so the command aborts unless `--force` is passed (which kills and replaces the existing server).

Solutions

  1. Run `astro dev stop` to stop the existing server, then start again
  2. Or replace it in one step: `astro dev --force`
  3. If the message persists unexpectedly, inspect `.astro/dev.json` in the project root — a PID reused by another process can fake a live server; delete the file or use `--force`

Example fix

# before
astro dev   # Another astro dev server is already running. URL: ... PID: ...

# after
astro dev stop
astro dev
# or replace directly:
astro dev --force
Defensive patterns

Strategy: validation

Validate before calling

import { existsSync, readFileSync } from 'node:fs';
import { join } from 'node:path';

// true when a live dev server holds the lock for this project root
function devServerIsRunning(root) {
  const lock = join(root, '.astro', 'dev.json');
  if (!existsSync(lock)) return false;
  try {
    const { pid } = JSON.parse(readFileSync(lock, 'utf8'));
    process.kill(pid, 0); // throws if the PID is gone
    return true;
  } catch {
    return false; // stale lock — safe to start
  }
}

if (devServerIsRunning(root)) {
  // run `astro dev stop` (or pass --force) before `astro dev`
}

Try / catch

import { spawnSync } from 'node:child_process';
const r = spawnSync('astro', ['dev'], { encoding: 'utf8' });
if (r.status !== 0 && /Another astro dev server is already running/.test(r.stderr)) {
  spawnSync('astro', ['dev', 'stop'], { stdio: 'inherit' });
  // retry `astro dev` once
}

Prevention

When it happens

Trigger: Running `astro dev` while a previous dev server for the same project root is still alive — another terminal, a detached/background server, a CI step, or a server spawned by an AI coding agent via `astro dev --background`.

Common situations: Forgotten server in another terminal or editor task; background server started by tooling that keeps running after the work session; shared machine/checkout where a teammate's server holds the lock.

Related errors


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

Appendix: source

Thrown at packages/astro/src/cli/dev/index.ts:221

		const inlineConfig = flagsToAstroInlineConfig(flags);
		return await devServer(inlineConfig);
	}

	const existingServer = await checkExistingServer(root);
	if (existingServer) {
		if (flags.force) {
			// --force: kill the existing server and replace it
			await killDevServer(root, existingServer);
		} else {
			const message = [
				'Another astro dev server is already running.',
				'',
				`  URL:  ${existingServer.url}`,
				`  PID:  ${existingServer.pid}`,
				'',
				`Run \`astro dev stop\` to stop it, or use \`astro dev --force\` to replace it.`,
			].join('\n');
			throw new Error(message);
		}
	}

	const inlineConfig = flagsToAstroInlineConfig(flags);
	const server = await devServer(inlineConfig);

	// Use Vite's resolved URL which accounts for host and protocol (http/https).
	const serverUrl = resolveLockFileUrl(server.resolvedUrls);
	if (serverUrl) {
		writeLockFile(root, {
			pid: process.pid,
			port: server.address.port,
			url: serverUrl,
			urls: server.resolvedUrls,
			background: !!process.env.ASTRO_DEV_BACKGROUND,
			startedAt: new Date().toISOString(),
		});

View on GitHub (pinned to 52e6c34790)