apache/beam · error · RuntimeError

Executable not found

Error message

Executable {} not found

What it means

processes.call wraps subprocess.call and converts OSError (typically FileNotFoundError from exec) into a RuntimeError naming the executable. It exists so Beam pipeline code gets a clearer error than a raw OSError when a required binary is missing.

Solutions

  1. Check `shutil.which('binary')` returns a path; install the missing executable (e.g. pip/apt install)
  2. Pass an absolute path to the executable if it lives outside PATH
  3. Fix PATH in the runtime environment (container image, cron, subprocess env)
  4. Check file permissions (chmod +x) if the binary exists but is not executable

Example fix

// before
processes.call('gsutil cp a b')  # gsutil not installed
// after
if not shutil.which('gsutil'):
    install_or_fail('gsutil')
processes.call('gsutil', 'cp', 'a', 'b')
Defensive patterns

Strategy: validation

Validate before calling

import shutil
if shutil.which('my-tool') is None:
    raise RuntimeError("Executable 'my-tool' not found; install it first")

Type guard

def executable_available(name: str) -> bool:
    return shutil.which(name) is not None

Try / catch

try:
    rc = processes.call('my-tool', 'arg')
except RuntimeError as e:
    if 'not found' in str(e):
        install_tool_or_fallback()
    else:
        raise

Prevention

When it happens

Trigger: Calling apache_beam.utils.processes.call('some-binary', ...) where the executable is not on PATH or the given path does not exist; any OSError from the subprocess (permission denied, ENOEXEC) also lands here.

Common situations: SDK workers (Dataflow containers) lacking the binary; typo'd executable name; not passing the full path; missing virtualenv activation in scripts.

Related errors


AI-assisted analysis of apache/beam@12126d8942 (2026-09-13). Data as JSON: /api/errors/7df92c5b34978739. Report an issue: GitHub.

Appendix: source

Thrown at sdks/python/apache_beam/utils/processes.py:53

PIPE = subprocess.PIPE
STDOUT = subprocess.STDOUT
CalledProcessError = subprocess.CalledProcessError

if TYPE_CHECKING:
  call = subprocess.call
  check_call = subprocess.check_call
  check_output = subprocess.check_output
  Popen = subprocess.Popen

else:

  def call(*args, **kwargs):
    if force_shell:
      kwargs['shell'] = True
    try:
      out = subprocess.call(*args, **kwargs)
    except OSError as e:
      raise RuntimeError("Executable {} not found".format(args[0])) from e
    except subprocess.CalledProcessError as error:
      if isinstance(args, tuple) and (args[0][2] == "pip"):
        raise RuntimeError( \
          "Full traceback: {}\n Pip install failed for package: {} \
          \n Output from execution of subprocess: {}" \
          .format(traceback.format_exc(), args[0][6], error. output)) from error
      else:
        raise RuntimeError("Full trace: {}\
           \n Output of the failed child process: {} " \
          .format(traceback.format_exc(), error.output)) from error
    return out

  def check_call(*args, **kwargs):
    if force_shell:
      kwargs['shell'] = True
    try:
      out = subprocess.check_call(*args, **kwargs)
    except OSError as e:

View on GitHub (pinned to 12126d8942)