{"record":{"id":"68af13132e80f5cf","repo":"denoland/deno","slug":"dev-server-exited-without-printing-a-url","errorCode":null,"errorMessage":"dev server exited without printing a URL","messagePattern":"dev server exited without printing a URL","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"cli/tools/desktop.rs","lineNumber":1226,"sourceCode":"      format!(\"failed to spawn HMR dev server: {:?}\", cmd_args)\n    })?;\n\n  let stdout = child.stdout.take().ok_or_else(|| {\n    deno_core::anyhow::anyhow!(\"failed to capture HMR dev server stdout\")\n  })?;\n  let mut lines = BufReader::new(stdout).lines();\n\n  let url = tokio::time::timeout(std::time::Duration::from_secs(15), async {\n    while let Ok(Some(line)) = lines.next_line().await {\n      let url = parse_dev_server_url(&line);\n      // Echo the dev server's own startup output (banner, URL, warnings) so\n      // it isn't swallowed while we scan for the URL.\n      log::info!(\"{line}\");\n      if let Some(url) = url {\n        return Ok(url);\n      }\n    }\n    deno_core::anyhow::bail!(\"dev server exited without printing a URL\")\n  })\n  .await\n  .map_err(|_| {\n    deno_core::anyhow::anyhow!(\n      \"{name} dev server with HMR did not start within 15s\"\n    )\n  })??;\n\n  // Keep forwarding the dev server's stdout so its HMR/compile logs stay\n  // visible after startup instead of being silently swallowed. The task ends\n  // on its own once the child is killed (on drop) and the pipe closes.\n  tokio::spawn(async move {\n    while let Ok(Some(line)) = lines.next_line().await {\n      log::info!(\"{line}\");\n    }\n  });\n\n  Ok((url, child))","sourceCodeStart":1208,"sourceCodeEnd":1244,"githubUrl":"https://github.com/denoland/deno/blob/f7822238cab635a3a19f99f493f675fa81a7f9d8/cli/tools/desktop.rs#L1208-L1244","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Scroll up and read the dev server's own output — it is echoed above this error and names the real failure.","Run the framework's dev command manually (e.g. `npm run dev`) in the project directory and fix whatever crash it reports.","Install dependencies (`npm install` / `pnpm install`) and clear the blocked port (`lsof -ti :5173 | xargs kill`).","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."],"exampleFix":"# before\ndeno desktop --hmr main.ts   # fails: vite crashed on missing deps\n\n# after\nnpm install && npm run dev   # confirm the banner prints a \"Local:\" URL, then:\ndeno desktop --hmr main.ts","handlingStrategy":"try-catch","validationCode":"# bash: prove the dev server starts and prints a Local: URL before using --hmr\nnpm install\n(npm run dev & DEV_PID=$!; sleep 5; kill $DEV_PID) 2>&1 | grep -q \"Local:\" \\\n  || { echo \"dev server did not print a Local: URL\" >&2; exit 1; }","typeGuard":null,"tryCatchPattern":"# In the wrapper script / CI, capture deno's output and branch on the failure\nset +e\nOUT=\"$(deno desktop --hmr main.ts 2>&1)\"; RC=$?\nset -e\nif [[ $RC -ne 0 ]]; then\n  if grep -q \"exited without printing a URL\" <<<\"$OUT\"; then\n    echo \"dev server failed to start — run 'npm run dev' to see the cause\" >&2\n  fi\n  exit $RC\nfi","preventionTips":["Always `npm install` before the first --hmr run on a fresh clone.","Keep the framework's default dev script (Vite prints the `Local:` line this feature scrapes).","Watch for and free occupied dev ports (5173 etc.) before launching.","Run the dev command by hand whenever you change vite.config or the dev script."],"tags":["desktop","hmr","dev-server","process-exit","framework"],"backgroundTag":"dev-server-startup-failure","analyzedSha":"f7822238cab635a3a19f99f493f675fa81a7f9d8","analyzedAt":"2026-08-20T13:07:44.778Z","contentChangedAt":"2026-08-20T13:07:44.778Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}