Skip to content

Fix local e2e: Astro 7 dev-server lock makes Playwright webServer exit early #200

Description

@schmug

Task

Make npm run test:e2e start reliably on a developer machine again. Since the Astro 7 upgrade (#176), the first local run frequently dies with Error: Process from config.webServer exited early. before a single test executes. Astro 7 added a dev-server lock: when a server is already listening on the target port, astro dev prints Dev server already running at http://localhost:4321 (pid N) and exits 0 instead of starting or falling through to another port. Playwright's webServer treats that immediate exit as fatal. After this is fixed, npm run test:e2e should pass from a cold start regardless of whether another Astro dev server is already running on the machine.

Context

Found while verifying the Astro 6 → 7 bump before merging #176. CI is not affected — playwright.config.ts:31 branches on process.env.CI and uses npm run build && npm run preview, which has no dev-server lock. Production is unaffected too (static build). This is local-DX only, but it matters more here than usual for two reasons:

  1. CLAUDE.md lists npm run test:e2e as a gate to run before claiming work complete. A future agent that hits this will read it as a real test failure and start debugging the wrong thing.
  2. This repo is routinely worked in multiple git worktrees at once, each with its own astro dev on the same default port 4321. During this investigation the port was held by a dev server belonging to an unrelated worktree (link-spotify-podcasts-5dfcea), which is precisely the condition that triggers the early exit. Astro 6 tolerated this; Astro 7 does not.

Reproduction

  1. Have any Astro dev server running on port 4321 — commonly one from a different worktree of this repo.
  2. In a second worktree, run npm run test:e2e.

Expected: Playwright starts or reuses a server and runs the suite.

Actual: Error: Process from config.webServer exited early. — zero tests run. A second invocation often succeeds, because by then a server is listening at the URL and reuseExistingServer short-circuits before the command is spawned. That intermittency is what makes this worth fixing rather than documenting.

Environment: astro 7.1.1, @playwright/test 1.61.0, macOS (darwin 25.5.0), local only (CI unset).

Pointers

  • playwright.config.ts:26-36 — the webServer block. command at :31, url at :32, reuseExistingServer: !process.env.CI at :33.
  • package.json:11"dev": "astro dev", the command that now early-exits.
  • package.json:14"preview": "astro preview", the CI-side path that does not have this problem.
  • npx astro dev --help — documents the Astro 7 surface added here: subcommands stop / status / logs, and the flags --background, --port, and --ignore-lock ("Start the dev server even if another one is already running, without checking or writing the lock file").

Suggested directions

Pick one; they are listed roughly easiest-to-most-robust. Do not do all of them.

  • Add --ignore-lock to the dev command used by Playwright. Smallest change, but two servers then race for the same port.
  • Give the e2e run its own port (e.g. astro dev --port 4331) and update webServer.url to match, so parallel worktrees stop colliding. Note url and the port must stay in sync.
  • Use the CI path locally too (npm run build && npm run preview). Most faithful to what CI verifies; costs a build on every local e2e run, and the comment at playwright.config.ts:27-30 explains why astro dev was chosen locally in the first place — preserve that reasoning if you change it.

Constraints

  • Do not change the CI branch of webServer.command — CI is green today and astro preview avoids the Vite dep-optimizer race described at playwright.config.ts:27-30.
  • Keep the comment block at playwright.config.ts:27-30 meaningful; if the local path changes, update the comment rather than deleting the rationale.
  • No new dependencies.
  • Must not require the developer to manually stop a server first — "run astro dev stop before testing" is a workaround, not a fix.

Acceptance criteria

  • With an Astro dev server already listening on 4321 (started from another worktree), a cold npm run test:e2e runs the suite instead of failing with webServer exited early.
  • With no server running, a cold npm run test:e2e still runs the suite.
  • Two consecutive npm run test:e2e invocations both pass, with no manual cleanup between them.
  • CI stays green — the CI branch of webServer.command is unchanged and E2E (Playwright) still passes on main.
  • No orphaned astro dev process is left listening after the run that did not exist before it.

Out of scope

  • The pre-existing iframe-embed e2e test that fails locally but passes in CI — a known local-only false alarm, unrelated to this.
  • Any change to astro.config.mjs optimizeDeps tuning.
  • Reworking how many ports the project uses generally, or introducing per-worktree port assignment as a broader convention.

Reasoning guidance

Reproduce the failure first — start a dev server on 4321, then run the suite in a second checkout and confirm you see webServer exited early before changing anything. The failure is order-dependent and a fix that is not verified against an already-occupied port will look like it works on the second run for the wrong reason.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions