windmill-labs/windmill · error
isolate result channel closed
Error message
isolate result channel closed
What it means
ExecutingIsolate::wait awaits a oneshot channel carrying the execution result from a dedicated (pre-warmed) Deno isolate thread. If the sender is dropped without sending — the isolate side vanished before producing a result — this error is thrown.
Source
Thrown at backend/windmill-runtime-nativets/src/dedicated.rs:25
ExecuteError, MainArgs, NativeAnnotation,
};
pub struct PrewarmedResult {
pub result: Result<Box<RawValue>, String>,
pub logs: String,
}
pub struct ExecutingIsolate {
result_rx: tokio::sync::oneshot::Receiver<PrewarmedResult>,
handle: tokio::task::JoinHandle<anyhow::Result<()>>,
}
impl ExecutingIsolate {
pub async fn wait(self) -> anyhow::Result<PrewarmedResult> {
let result = self
.result_rx
.await
.map_err(|_| anyhow::anyhow!("isolate result channel closed"))?;
self.handle
.await
.map_err(|e| anyhow::anyhow!("isolate thread panicked: {e}"))??;
Ok(result)
}
}
pub struct PrewarmedIsolate {
args_tx: Option<tokio::sync::oneshot::Sender<String>>,
result_rx: Option<tokio::sync::oneshot::Receiver<PrewarmedResult>>,
ready_rx: Option<tokio::sync::oneshot::Receiver<()>>,
handle: Option<tokio::task::JoinHandle<anyhow::Result<()>>>,
}
/// Parse a JSON args object and reorder into positional args matching `arg_names`.
fn args_to_positional(args_json: &str, arg_names: &[String]) -> Vec<Option<Box<RawValue>>> {
let map: HashMap<String, Box<RawValue>> = serde_json::from_str(args_json).unwrap_or_default();
arg_namesView on GitHub (pinned to e474e8803c)
Solutions
- Check worker logs immediately before this error for isolate/runtime shutdown or panic messages
- Retry the script execution; a transient isolate teardown is often one-off
- Check for OOM or memory-limit kills of the worker process (isolate memory limits)
- Upgrade windmill-runtime-nativets / backend to the latest version to get isolate lifecycle fixes
- If reproducible, file a bug with the script and worker logs
Example fix
// caller-side hardening
match isolate.wait().await {
Ok(result) => result,
Err(e) if e.to_string().contains("channel closed") => {
// recreate a fresh prewarmed isolate and retry once
let mut iso = PrewarmedIsolate::new(...)?;
iso.wait_ready().await?;
iso.start(args).await?.wait().await?
}
Err(e) => return Err(e),
} Defensive patterns
Strategy: retry
Try / catch
match exec.wait().await {
Err(e) if e.to_string().contains("isolate result channel closed") => {
// recreate isolate and retry once
recreate_and_run(args).await
}
other => other,
} Prevention
- Gracefully drain in-flight isolates before worker restarts/shutdowns
- Keep isolate memory limits generous to avoid kills mid-execution
- Retry script runs at the job-queue level where possible
- Watch for clusters of this error — it usually signals runtime lifecycle bugs, not bad scripts
When it happens
Trigger: Calling .wait() on an ExecutingIsolate when the isolate's task dropped result_tx without sending: the isolate aborted, was dropped early, or the runtime shut the isolate down before completion.
Common situations: Worker shutdown or SIGKILL while a nativets script is executing; an isolate crash path that skips sending a result; bugs in the dedicated-isolate lifecycle under concurrent load.
Related errors
- isolate thread panicked: {e}
- isolate failed during pre-warm
- preprocessor function is missing
- ${main_name} function is missing
- errorMsg ?? 'Job failed'
AI-assisted analysis of windmill-labs/windmill@e474e8803c (2026-09-03).
Data as JSON: /api/errors/a8717c9a07f1a5df.
Report an issue: GitHub.