denoland/deno · error

esbuild exited before the rebuild completed

Error message

esbuild exited before the rebuild completed

What it means

In watch mode, a rebuild sends a rebuild request to the persistent esbuild child process and then awaits its on_end event over a channel; recv() returning None means every sender was dropped - the esbuild process exited (crashed, was killed, binary vanished) before the rebuild finished (cli/tools/bundle/mod.rs:1176-1189).

Source

Thrown at cli/tools/bundle/mod.rs:1185

    Ok(response)
  }

  async fn rebuild(&mut self) -> Result<BuildResponse, AnyError> {
    match self.mode {
      BundlingMode::OneShot => {
        panic!("rebuild not supported for one-shot mode")
      }
      BundlingMode::Watch => {
        log::trace!("sending rebuild request");
        let _response = self
          .client
          .send_rebuild_request(0)
          .await
          .context("failed to send rebuild request to esbuild")?
          .map_err(|e| message_to_error(&e, &self.cwd))?;
        let response = self.on_end_rx.recv().await.ok_or_else(|| {
          deno_core::anyhow::anyhow!(
            "esbuild exited before the rebuild completed"
          )
        })?;
        Ok(response.into())
      }
    }
  }

  async fn reload_specifiers(
    &mut self,
    changed_paths: &[PathBuf],
  ) -> Result<(), AnyError> {
    self.reload_html_entrypoints(changed_paths)?;
    self.plugin_handler.reload_specifiers(changed_paths).await?;
    Ok(())
  }

  fn reload_html_entrypoints(

View on GitHub (pinned to 89f33cbef2)

Solutions

  1. Restart the watch session (deno bundle --watch ...) - the child is respawned on startup
  2. Check for OOM kills: dmesg | grep -i oom or container memory limits; raise the limit or reduce --minify/workload
  3. Delete the cached esbuild binary directory under DENO_DIR so the next start re-downloads a clean copy
  4. If it recurs on current Deno with a reproducible trigger, report to denoland/deno with the esbuild version pin
Defensive patterns

Strategy: retry

Validate before calling

# Verify the cached esbuild binary is present and executable before long watch sessions
BIN_DIR=$(deno info 2>/dev/null | sed -n 's/.*DENO_DIR[^ ]* //p')
find "${DENO_DIR:-$HOME/.cache/deno}" -name esbuild -type f -executable -print 2>/dev/null || echo 'no cached esbuild binary - expect first-run download'

Try / catch

# Supervised watch: restart the session when esbuild dies mid-rebuild
until deno bundle --watch --outdir dist src/main.ts; do
  echo 'watch session died; restarting in 3s' >&2
  if dmesg 2>/dev/null | grep -qi 'oom.*esbuild'; then echo 'OOM kill detected - raise memory limit' >&2; exit 1; fi
  sleep 3
done

Prevention

When it happens

Trigger: The esbuild child crashes mid-rebuild (native crash, OOM kill on constrained CI containers); the DENO_DIR-cached esbuild binary is deleted or corrupted while watching; system shutting down and the process group being torn down.

Common situations: Long watch sessions on memory-limited containers/CI where the kernel OOM-kills esbuild; antivirus/cleanup tools purging cache directories during the session; corrupted binary after a partial install (the atomic-rename logic guards this, but external deletion is still possible).

Related errors


AI-assisted analysis of denoland/deno@89f33cbef2 (2026-08-16). Data as JSON: /api/errors/fba65545d4173b5b. Report an issue: GitHub.