{"record":{"id":"35dec4c16a37383b","repo":"windmill-labs/windmill","slug":"cannot-take-stderr-from-uv-install-proccess","errorCode":null,"errorMessage":"Cannot take stderr from uv_install_proccess","messagePattern":"Cannot take stderr from uv_install_proccess","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"backend/windmill-worker/src/python_executor.rs","lineNumber":2998,"sourceCode":"                        &job_id,\n                        w_id,\n                        format!(\n                            \"\\nError while spawning proccess:\\n{e}\",\n                        ),\n                        &conn,\n                    )\n                    .await;\n                    pids.lock().await.get_mut(i).and_then(|e| e.take());\n                    return Err(Error::from(e));\n                }\n            };\n\n            let (mut stderr_buf, mut stdout_buf) = Default::default();\n            let (mut stderr_pipe, mut stdout_pipe) = (\n                uv_install_proccess\n                    .stderr()\n                    .take()\n                    .ok_or(anyhow!(\"Cannot take stderr from uv_install_proccess\"))?,\n                uv_install_proccess\n                    .stdout()\n                    .take()\n                    .ok_or(anyhow!(\"Cannot take stdout from uv_install_proccess\"))?\n            );\n            let (stderr_future, stdout_future) = (\n                stderr_pipe.read_to_string(&mut stderr_buf),\n                stdout_pipe.read_to_string(&mut stdout_buf)\n            );\n\n            if let Some(pid) = pids.lock().await.get_mut(i) {\n                *pid = uv_install_proccess.id();\n                #[cfg(unix)]\n                if let Err(e) = uv_install_proccess\n                  .id()\n                  .ok_or(Error::InternalErr(format!(\n                    \"failed to get PID for python installation process: {}\",\n                    &req","sourceCodeStart":2980,"sourceCodeEnd":3016,"githubUrl":"https://github.com/windmill-labs/windmill/blob/e474e8803ce2ff5c2df09a58dab51d45f5c922ca/backend/windmill-worker/src/python_executor.rs#L2980-L3016","documentation":"handle_python_reqs spawns a `uv pip install` child process and then calls .stderr().take() on the tokio::process::Child to grab the piped stderr handle for async draining. take() returns None if the child has no stderr pipe — i.e. it was spawned without Stdio::piped() for stderr, or the handle was already taken. The code maps that None to \"Cannot take stderr from uv_install_proccess\".","triggerScenarios":"The uv install child process was spawned without its stderr configured as piped (a code/config regression in how the Command is built), or the stderr handle was already consumed by a previous take() on the same Child.","commonSituations":"A Windmill version regression changing process spawn options; another code path already took the stderr pipe (double invocation); modifications to uv invocation (e.g. switching to a shell wrapper) that dropped piped stdio; uv binary replaced by a wrapper script that alters spawn behavior in custom workers.","solutions":["Ensure the uv Command is built with .stderr(Stdio::piped()) (and .stdout(Stdio::piped())) before spawn.","Take each pipe handle exactly once; verify no other code path consumes the Child's stderr before this point.","If you patched the executor or wrapped uv, restore the piped-stdio spawn options.","Upgrade/downgrade Windmill to a version where the uv spawn options are intact, and report the regression."],"exampleFix":"// before\nlet mut uv_install_proccess = Command::new(\"uv\").args([...]).spawn()?;\n// after\nuse std::process::Stdio;\nlet mut uv_install_proccess = Command::new(\"uv\")\n    .args([...])\n    .stdout(Stdio::piped())\n    .stderr(Stdio::piped())\n    .spawn()?;","handlingStrategy":"fallback","validationCode":null,"typeGuard":null,"tryCatchPattern":"// worker-side: surface a clear regression message instead of a bare None\nmatch child.stderr.take() {\n  Some(p) => p,\n  None => return Err(anyhow!(\"uv spawn must set stderr(Stdio::piped()); check executor Command construction\")),\n}","preventionTips":["Always spawn subprocesses with .stdout(Stdio::piped()).stderr(Stdio::piped()) when output will be drained","Never take() the same pipe handle twice; restructure retry logic to respawn the child","Add a CI test that runs a minimal uv install path after executor changes"],"tags":["python","subprocess","uv","stdio"],"backgroundTag":"missing-piped-stdio","analyzedSha":"e474e8803ce2ff5c2df09a58dab51d45f5c922ca","analyzedAt":"2026-09-03T12:38:19.024Z","contentChangedAt":"2026-09-03T12:38:19.024Z","schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}