{"record":{"id":"efa2928d4b834da6","repo":"denoland/deno","slug":"failed-to-capture-hmr-dev-server-stdout","errorCode":null,"errorMessage":"failed to capture HMR dev server stdout","messagePattern":"failed to capture HMR dev server stdout","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"cli/tools/desktop.rs","lineNumber":1212,"sourceCode":"  name: &str,\n  cmd_args: &[String],\n  cwd: &Path,\n) -> Result<(String, tokio::process::Child), AnyError> {\n  use tokio::io::AsyncBufReadExt;\n  use tokio::io::BufReader;\n\n  let mut child = tokio::process::Command::new(&cmd_args[0])\n    .args(&cmd_args[1..])\n    .current_dir(cwd)\n    .stdout(std::process::Stdio::piped())\n    .kill_on_drop(true)\n    .spawn()\n    .with_context(|| {\n      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!(","sourceCodeStart":1194,"sourceCodeEnd":1230,"githubUrl":"https://github.com/denoland/deno/blob/f7822238cab635a3a19f99f493f675fa81a7f9d8/cli/tools/desktop.rs#L1194-L1230","documentation":"Internal invariant in the desktop dev flow: after spawning the HMR dev server with piped stdout, Deno takes the pipe handle to scan output for the startup URL. If the handle was already taken (or stdio wasn't actually piped), capture fails and the dev flow aborts immediately after a successful spawn.","triggerScenarios":"Only reachable through a deno bug or race in the desktop dev-server startup path — `stdout.take()` returning `None` right after a spawn configured with `.stdout(piped())` should be impossible.","commonSituations":"A race during process spawn on unusual platforms, or a regression in a specific deno version's desktop tooling.","solutions":["Retry the desktop dev command — a transient spawn race is the likeliest cause","Update deno; if it reproduces consistently, report with the exact command, platform, and deno version"],"exampleFix":null,"handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"Treat as transient: retry the desktop dev command once; if it fails identically, stop retrying and report with command, platform, and deno version.","preventionTips":["Update deno when desktop dev tooling misbehaves","Retry dev-server startup once before deeper investigation"],"tags":["desktop","dev-server","internal","process"],"backgroundTag":"process-stdout-capture-failed","analyzedSha":"f7822238cab635a3a19f99f493f675fa81a7f9d8","analyzedAt":"2026-08-20T13:07:44.778Z","contentChangedAt":"2026-08-20T13:07:44.778Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}