withastro/astro · error · Error
Another astro dev server is already running. URL: ${exis
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
Astro writes a lock file (containing PID, port, URL) to the project root's `.astro` directory when `astro dev` starts. On the next `astro dev` invocation it reads that file, checks whether the recorded PID is still alive, and refuses to start a second dev server against the same root. This prevents port/PID conflicts and ensures only one HMR process owns a project at a time.
Source
Thrown at packages/astro/src/cli/dev/index.ts:221
const inlineConfig = flagsToAstroInlineConfig(flags);
return await devServer(inlineConfig);
}
const existingServer = 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 d081033d5f)
Solutions
- Run `astro dev stop` to stop the existing server (uses the PID from the lock file).
- Run `astro dev --force` to kill the existing server and replace it in one step.
- If the PID is stale but the process appears gone, manually kill the process holding the port and delete the lock file in `.astro/`.
- Check `ps -p <PID>` to confirm whether the recorded PID is actually a live Astro process before killing.
Example fix
// before $ astro dev // after $ astro dev stop && astro dev // or $ astro dev --force
Defensive patterns
Strategy: validation
Validate before calling
import { existsSync, readFileSync } from 'node:fs';
import { join } from 'node:path';
function isDevServerRunning(root) {
const lockPath = join(root, '.astro', 'dev.lock');
if (!existsSync(lockPath)) return null;
try {
const { pid, url } = JSON.parse(readFileSync(lockPath, 'utf-8'));
if (pid && process.kill(pid, 0)) return { pid, url };
} catch {}
return null;
}
const existing = isDevServerRunning(process.cwd());
if (existing) console.log(`Already running at ${existing.url} (PID ${existing.pid}) — pass --force.`); Prevention
- Always stop dev servers with `astro dev stop` before starting a new one.
- Use `astro dev --force` in scripts that may reuse a workspace.
- Add `astro dev stop` to your editor/CI teardown steps.
When it happens
Trigger: Running `astro dev` when `checkExistingServer(root, 'dev')` returns a non-null result (a live process holding the lock for that root). The `--force` flag bypasses this by calling `killDevServer` first.
Common situations: A previous `astro dev` was left running in another terminal or editor; a crashed session left a stale-but-live process; CI reused a workspace without tearing down the prior server; you switched branches but forgot the old server is still bound to the project root.
Related errors
- Another astro preview server is already running. URL: ${
- `--ignore-lock` cannot be used together with `--background`.
- Unknown error parsing tsconfig.json or jsconfig.json. Could
- ${integration} does not appear to be a valid package name!
- No problem! Find our official integrations at https://astro.
AI-assisted analysis of withastro/astro@d081033d5f (2026-08-12).
Data as JSON: /api/errors/bef60b52ba0a896a.
Report an issue: GitHub.