mvanhorn/last30days-skill · critical
engine: %s not found on PATH (need Python %s+, install from
Error message
engine: %s not found on PATH (need Python %s+, install from %s; current GOOS=%s)
What it means
Thrown by engine.resolvePython when the MCP server cannot find a 'python3' executable on the process PATH via exec.LookPath. The Go MCP wrapper shells out to python3 to run the embedded last30days.py engine, so a missing interpreter is fatal before any research starts. The message deliberately includes the minimum version (3.12), the install URL, and runtime.GOOS because Windows installs frequently expose only 'python' (or the py launcher), not 'python3'. Note that only the binary name is checked here — the version constraint itself is enforced later by the Python engine, not by this lookup.
Source
Thrown at mcp/internal/engine/run.go:127
return res, fmt.Errorf("engine: subprocess exceeded %s timeout", timeout)
}
return res, fmt.Errorf("engine: subprocess exited with code %d", res.ExitCode)
}
return res, fmt.Errorf("engine: subprocess failed to start: %w", err)
}
// resolvePython returns an absolute path to the interpreter or an error
// naming the install URL. If the caller supplied a path we trust it - tests
// rely on this to inject a stub. Otherwise we look up python3 on PATH.
func resolvePython(override string) (string, error) {
if override != "" {
return override, nil
}
path, err := exec.LookPath(DefaultPythonBinary)
if err == nil {
return path, nil
}
return "", fmt.Errorf(
"engine: %s not found on PATH (need Python %s+, install from %s; current GOOS=%s)",
DefaultPythonBinary, MinPythonVersion, PythonInstallURL, runtime.GOOS,
)
}
func resolveTimeout(explicit time.Duration) time.Duration {
if explicit > 0 {
return explicit
}
if raw := os.Getenv(TimeoutEnvOverride); raw != "" {
if d, err := time.ParseDuration(raw); err == nil && d > 0 {
return d
}
// Accept bare integer seconds (e.g. "300") as documented.
if secs, err := strconv.Atoi(raw); err == nil && secs > 0 {
return time.Duration(secs) * time.Second
}
}View on GitHub (pinned to c7460f6114)
Solutions
- Install Python 3.12+ from https://www.python.org/downloads/ and ensure its bin directory is on the PATH of the process that launches the MCP server (not just your interactive shell).
- On Windows, create a python3 alias: install the python.org build and run 'py -0' to confirm, then either copy/symlink python.exe to python3.exe in the same directory (and put that directory on PATH), or use winget install python.python.3.12 which can expose both names.
- For GUI-launched MCP hosts (Claude Desktop), put the interpreter somewhere system-wide (e.g. /usr/local/bin/python3 or C:\Program Files\Python312\) and restart the host so it inherits the updated PATH.
- If you embed this package, bypass PATH entirely by setting RunOptions.PythonPath to an absolute interpreter path (resolve it yourself or from an env var you control).
- Verify with 'which python3' in the same environment the server runs under, and check 'python3 --version' is >= 3.12.
Example fix
// before: relying on PATH lookup in a GUI-launched host
res, err := engine.Run(ctx, engine.RunOptions{CacheDir: cacheDir, Args: args})
// -> "engine: python3 not found on PATH (need Python 3.12+, ...)"
// after: resolve an explicit interpreter (env-configurable) and pass it
pythonPath := os.Getenv("LAST30DAYS_PYTHON")
if pythonPath == "" {
if p, err := exec.LookPath("python3"); err == nil {
pythonPath = p
}
}
res, err := engine.Run(ctx, engine.RunOptions{
PythonPath: pythonPath, // absolute path; empty still falls back to PATH
CacheDir: cacheDir,
Args: args,
}) Defensive patterns
Strategy: validation
Validate before calling
// Run before starting the MCP server / first engine.Run call.
func checkInterpreter(override string) error {
if override != "" {
if _, err := os.Stat(override); err != nil {
return fmt.Errorf("configured Python path %q not usable: %w", override, err)
}
return nil
}
path, err := exec.LookPath("python3")
if err != nil {
return fmt.Errorf("python3 missing on PATH (GOOS=%s); install Python 3.12+ and restart the host", runtime.GOOS)
}
out, err := exec.Command(path, "--version").Output()
if err != nil || !strings.Contains(string(out), "Python 3.") {
return fmt.Errorf("interpreter at %s is not Python 3.x: %s", path, out)
}
return nil
} Try / catch
// engine.Run returns this as a regular Go error; check it before using the result.
res, err := engine.Run(ctx, opts)
if err != nil {
if strings.Contains(err.Error(), "not found on PATH") {
// environment problem: guide user to install Python 3.12+ or set PythonPath
return fmt.Errorf("setup required: %w", err)
}
return err // other engine failures (timeout, non-zero exit) — see res.Stderr for detail
} Prevention
- Smoke-test the deployment environment with 'python3 --version' (expect >= 3.12) using the same launch context as the MCP server (GUI host, service manager, container entrypoint).
- Set RunOptions.PythonPath to an absolute interpreter resolved from an env var you control, so behavior does not depend on inherited PATH.
- On Windows, ensure a python3 alias exists (python.org installer + PATH, or winget) before deploying the MCP server.
- Add a startup preflight: fail fast at server boot with a clear message instead of erroring on the first research call.
When it happens
Trigger: Calling engine.Run (directly, or through the MCP 'research'/'preflight' tools) with RunOptions.PythonPath empty while exec.LookPath("python3") fails. Typical concrete cases: running the MCP server on Windows where only python.exe exists; a GUI-launched Claude Desktop whose environment PATH omits Homebrew's /opt/homebrew/bin or ~/.local/bin; a container or sandbox image without Python installed; PATH stripped by a systemd/launchd service.
Common situations: Windows hosts (python3.exe rarely exists even when Python is installed — the python.org installer creates python.exe and the py launcher); macOS GUI apps not inheriting shell PATH additions; Docker/distroless images; CI runners where Python is present under a different name or via pyenv shims not activated; MCP clients that spawn the server with a sanitized environment.
Related errors
- format must be 'text' or 'json', got %q
- %s is required
- %s must be a non-empty string
- emit must be 'compact' or 'html', got %q
- %s must be a boolean
AI-assisted analysis of mvanhorn/last30days-skill@c7460f6114 (2026-08-15).
Data as JSON: /api/errors/613df383bf31cfd2.
Report an issue: GitHub.