denoland/deno · error

dev server exited without printing a URL

Error message

dev server exited without printing a URL

What it means

Thrown when a framework dev server spawned for `deno desktop --hmr` (the framework's hmr_command, e.g. Vite via npm) exits before printing a startup line that matches the `Local: <http(s)://...>` pattern scraped by parse_dev_server_url. The stdout lines are echoed while scanning, so the framework's own error output usually appears just above this message. A sibling error covers the 15s timeout case where the process stays alive but never prints a URL.

Solutions

  1. Scroll up and read the dev server's own output — it is echoed above this error and names the real failure.
  2. Run the framework's dev command manually (e.g. `npm run dev`) in the project directory and fix whatever crash it reports.
  3. Install dependencies (`npm install` / `pnpm install`) and clear the blocked port (`lsof -ti :5173 | xargs kill`).
  4. If using a custom hmr_command whose output differs from Vite's `Local:` line, switch to a Vite-based dev server or extend the detection upstream.

Example fix

# before
deno desktop --hmr main.ts   # fails: vite crashed on missing deps

# after
npm install && npm run dev   # confirm the banner prints a "Local:" URL, then:
deno desktop --hmr main.ts
Defensive patterns

Strategy: try-catch

Validate before calling

# bash: prove the dev server starts and prints a Local: URL before using --hmr
npm install
(npm run dev & DEV_PID=$!; sleep 5; kill $DEV_PID) 2>&1 | grep -q "Local:" \
  || { echo "dev server did not print a Local: URL" >&2; exit 1; }

Try / catch

# In the wrapper script / CI, capture deno's output and branch on the failure
set +e
OUT="$(deno desktop --hmr main.ts 2>&1)"; RC=$?
set -e
if [[ $RC -ne 0 ]]; then
  if grep -q "exited without printing a URL" <<<"$OUT"; then
    echo "dev server failed to start — run 'npm run dev' to see the cause" >&2
  fi
  exit $RC
fi

Prevention

When it happens

Trigger: The dev-server command fails immediately: missing node_modules (`npm install` not run), bad `dev` script, unsupported Node version, port already in use, or the framework crashed on startup. Also triggered when a non-Vite dev command prints its URL in a different format that the `Local:` regex does not match — but only if the process then exits; a live server that never matches hits the 15s timeout instead.

Common situations: Fresh clone where dependencies are not installed; `vite.config` error or syntax error in the entry; port 5173 occupied by a stale process; custom dev script (webpack-dev-server, `deno serve`) whose banner does not say `Local:`; CI without a TTY where the dev server refuses to start.

Related errors


AI-assisted analysis of denoland/deno@f7822238ca (2026-08-20). Data as JSON: /api/errors/68af13132e80f5cf. Report an issue: GitHub.

Appendix: source

Thrown at cli/tools/desktop.rs:1226

      format!("failed to spawn HMR dev server: {:?}", cmd_args)
    })?;

  let stdout = child.stdout.take().ok_or_else(|| {
    deno_core::anyhow::anyhow!("failed to capture HMR dev server stdout")
  })?;
  let mut lines = BufReader::new(stdout).lines();

  let url = tokio::time::timeout(std::time::Duration::from_secs(15), async {
    while let Ok(Some(line)) = lines.next_line().await {
      let url = parse_dev_server_url(&line);
      // Echo the dev server's own startup output (banner, URL, warnings) so
      // it isn't swallowed while we scan for the URL.
      log::info!("{line}");
      if let Some(url) = url {
        return Ok(url);
      }
    }
    deno_core::anyhow::bail!("dev server exited without printing a URL")
  })
  .await
  .map_err(|_| {
    deno_core::anyhow::anyhow!(
      "{name} dev server with HMR did not start within 15s"
    )
  })??;

  // Keep forwarding the dev server's stdout so its HMR/compile logs stay
  // visible after startup instead of being silently swallowed. The task ends
  // on its own once the child is killed (on drop) and the pipe closes.
  tokio::spawn(async move {
    while let Ok(Some(line)) = lines.next_line().await {
      log::info!("{line}");
    }
  });

  Ok((url, child))

View on GitHub (pinned to f7822238ca)