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

  1. 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).
  2. 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.
  3. 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.
  4. 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).
  5. 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

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


AI-assisted analysis of mvanhorn/last30days-skill@c7460f6114 (2026-08-15). Data as JSON: /api/errors/613df383bf31cfd2. Report an issue: GitHub.