fix(python): resolve real interpreter for Process runner without pythonVersion#394
Conversation
…onVersion Python Script/Commands tasks using the Process task runner failed with "/bin/sh: python: not found" (exit 127) on hosts that ship only python3 (Debian/Ubuntu, the standard Kestra worker image). The interpreter was only resolved to an installed binary when pythonVersion was explicitly set; otherwise it stayed the literal "python". Now the Process runner always resolves an installed interpreter, probing python3 before python via PackageManagerType.PIP.getPythonPath when no pythonVersion is set, without triggering a managed-Python download for the no-dependency case. The Docker/K8s path is unchanged. Also updates the all_python sanity-check flow (python3 main.py) and adds a regression test. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Tests report quick summary:success ✅ > tests: 239, success: 239, skipped: 0, failed: 0 unfold for details
|
🧪 QA Report — ✅ PASSEdition: OSS | Branch: develop (2.0.0-SNAPSHOT) Verified end-to-end in the Kestra UI (Process runner executes on the worker host). This QA host has
Before → after (root cause): previously the plugin built the command as Flow 1:
|
| Task | Runner | Status | Duration |
|---|---|---|---|
script_process_default |
Process (no pythonVersion) | SUCCESS | 11.04s |
script_process_pip |
Process (PIP) | SUCCESS | 0.07s |
script_docker_default |
Docker | SUCCESS | 0.72s |
| Total | SUCCESS | 12.01s |
Logs synthesis
script_process_default→ executedpython3.14 /…/script.py; loggedResolved interpreter: /usr/bin/python3.14,Version: 3.14.4. Nopython: not found.script_process_pip→ executedpython3.14 …;Resolved interpreter (PIP): /usr/bin/python3.14.script_docker_default→ executedpython /…/script.pyinside the container (unchanged Docker path — image symlinkspython), confirming no regression.
Flow 2: all_python (✅ SUCCESS) — literal reproduction of the reported failure
Flow YAML (repo sanity-check flow, with the python3 main.py fix)
# plugin-script-python/src/test/resources/sanity-checks/all_python.yaml
# Key tasks (the two that failed in the original report):
- id: python_script_process
type: io.kestra.plugin.scripts.python.Script
taskRunner:
type: io.kestra.plugin.core.runner.Process
script: |
print("Hello, World from a Process Task Runner")
- id: python_commands_process
type: io.kestra.plugin.scripts.python.Commands
taskRunner:
type: io.kestra.plugin.core.runner.Process
inputFiles:
main.py: |
print("Hello, World from the Commands Task and Process Task Runner")
commands:
- python3 main.py # <-- changed from `python main.py`Gantt
| Task | Status | Duration |
|---|---|---|
python_script |
SUCCESS | 0.61s |
python_script_process ⭐ |
SUCCESS | 0.10s |
python_commands |
SUCCESS | 0.49s |
python_commands_process ⭐ |
SUCCESS | 0.09s |
python_script_logs |
SUCCESS | 25.40s |
python_script_metrics |
SUCCESS | 21.44s |
python_script_outputs |
SUCCESS | 13.85s |
python_commands_etl_outputs |
SUCCESS | 1.34s |
assert |
SUCCESS | 0.04s |
| Total | SUCCESS | ~65s |
⭐ = tasks that failed with python: not found (exit 127) in the original report.
Logs synthesis
python_script_process→ executedpython3.14 /…/script.py→Hello, World from a Process Task Runner.python_commands_process→ executedpython3 main.py→Hello, World from the Commands Task and Process Task Runner.assertpassed allexitCode == 0conditions;python_commands_etl_outputsproducedprocessed_orders.csv.
Outputs synthesis — python_script_outputs and python_commands_etl_outputs returned the total output and the processed_orders.csv output file, as asserted.
Timeouts / notes: none. The _logs/_metrics/_outputs tasks take 13–25s (network: pip install, HuggingFace dataset download, pydata image) but all completed and are unrelated to this fix. Topology/artifact testing not applicable (this PR does not add plugin artifacts).

What changes are being made and why?
Python
Script/Commandstasks running on theProcesstask runner failed with/bin/sh: python: not found(exit code 127) on hosts that ship onlypython3(Debian/Ubuntu, and the standard Kestra worker image).Root cause: In
PythonEnvironmentManager.setup(), the interpreter was only resolved to an actual installed binary whenpythonVersionwas explicitly set. When it was unset (the common case) on a Process runner, the interpreter stayed the literal"python", which doesn't exist onpython3-only hosts. The Docker default runner was unaffected becausepython:3.13-slimsymlinkspython.Fix: The Process runner now always resolves an installed interpreter. When
pythonVersionis set, behavior is unchanged (resolved through the configured package manager, which may provision a managed Python viauv). When it's unset, we probe for an already-installed interpreter (python3→python) viaPackageManagerType.PIP.getPythonPath, avoiding a managed-Python download just to run a dependency-free script. The Docker/Kubernetes path is untouched.Also:
all_pythonsanity-check flow:python_commands_processnow usespython3 main.py(user-authored command, not routed through the resolver).PythonEnvironmentManagerTestregression test asserting a Process-runner Script with nopythonVersionresolves to a runnable, non-"python"interpreter.How the changes have been QAed?
PythonEnvironmentManagerTestpasses.ScriptTest.task[PROCESS]— the exact case that reproduced the reported failure — now passes (previously exit 127).On a
python3-only host this failed withpython: not foundbefore the fix and now succeeds.Contributor Checklist ✅