The Error
You called a shell command from Python using subprocess.run(), subprocess.check_call(), or subprocess.check_output() and got:
subprocess.CalledProcessError: Command '['git', 'push']' returned non-zero exit status 1
This happens when the command exited with a non-zero status code β it failed. Python raises CalledProcessError only when you explicitly ask it to check for failure: either via check=True, or by using check_call/check_output.
Why It Happens
Every process exits with a status code. Zero means success; anything else means failure. By default, subprocess.run() quietly returns a result object even when the command fails β no exception raised. Pass check=True, and Python turns a non-zero exit code into a CalledProcessError.
Typical triggers:
- The command itself failed (e.g.,
git pushrejected,pip installpackage not found) - Wrong arguments passed to the command
- Missing dependency or binary not in PATH
- Permission denied on a file or directory
- Script explicitly calls
sys.exit(1)orexit 1
Step 1 β Read the Actual Error Output
Before anything else: read what the command printed. Subprocess captures nothing by default β output goes straight to the terminal and disappears. Capture it like this:
import subprocess
result = subprocess.run(
['git', 'push'],
capture_output=True,
text=True
)
print('STDOUT:', result.stdout)
print('STDERR:', result.stderr)
print('Exit code:', result.returncode)
Nine times out of ten, the real error message is sitting in stderr. Read it β most fixes become obvious at this point.
Step 2 β Catch the Exception Properly
When using check=True, wrap the call in a try/except to handle failures without crashing:
import subprocess
try:
result = subprocess.run(
['git', 'push'],
check=True,
capture_output=True,
text=True
)
print(result.stdout)
except subprocess.CalledProcessError as e:
print(f'Command failed with exit code {e.returncode}')
print(f'STDOUT: {e.stdout}')
print(f'STDERR: {e.stderr}')
The exception object e carries .returncode, .stdout, and .stderr. Those three attributes tell you exactly what went wrong.
Step 3 β Fix Based on the Root Cause
Command not found
Stderr says command not found or No such file or directory? The binary isn't in PATH. Check first before running:
import shutil
if not shutil.which('mycommand'):
raise EnvironmentError('mycommand is not installed or not in PATH')
Or skip the lookup entirely and use the full path:
subprocess.run(['/usr/local/bin/mycommand', '--flag'], check=True)
Shell features (pipes, redirects, wildcards)
Passing a list like ['ls', '-la', '|', 'grep', 'txt'] doesn't work β the pipe character is treated as a literal argument, not a shell operator. Use shell=True with a string instead:
# Wrong β | is not an argument
subprocess.run(['ls', '-la', '|', 'grep', 'txt'], check=True)
# Correct
subprocess.run('ls -la | grep txt', shell=True, check=True)
One hard rule: never pass user-supplied strings to shell=True. That's a shell injection waiting to happen. Stick to hardcoded strings or sanitized input.
Working directory issues
Commands like npm install or make need to run from a specific directory. Use cwd instead of os.chdir():
subprocess.run(['npm', 'install'], check=True, cwd='/path/to/project')
Missing environment variables
Some scripts rely on env vars that aren't set in the subprocess environment. Pass them explicitly:
import os
env = os.environ.copy()
env['MY_VAR'] = 'value'
subprocess.run(['myscript.sh'], check=True, env=env)
Permission denied
Script not executable? Two options. Either add the execute bit first:
subprocess.run(['chmod', '+x', 'myscript.sh'], check=True)
subprocess.run(['./myscript.sh'], check=True)
Or skip the permissions issue entirely and invoke the interpreter directly:
subprocess.run(['bash', 'myscript.sh'], check=True)
Step 4 β Decide: check=True or Manual Check?
Not every failure deserves an exception. When a non-zero exit code is a normal outcome β checking connectivity, testing if a process is alive β just inspect returncode directly:
result = subprocess.run(['ping', '-c', '1', '8.8.8.8'], capture_output=True)
if result.returncode == 0:
print('Host is reachable')
else:
print('Host unreachable')
Reserve check=True for commands where failure should stop execution. Skip it when failure is a valid path.
Verify the Fix
Run the call in isolation and confirm the exit code comes back as 0:
result = subprocess.run(
['your', 'command', 'here'],
capture_output=True,
text=True
)
print('Exit code:', result.returncode) # Should print: Exit code: 0
print(result.stdout)
Exit code 0 β you're done. Still non-zero β check result.stderr for the next clue and repeat.
Quick Reference
- Capture stderr first β
capture_output=True, text=Truereveals the real failure reason - Use
check=Truefor critical commands where failure should halt execution - Drop
check=Truewhen a non-zero exit code is an expected, valid result - Never combine
shell=Truewith untrusted input β pass a list of arguments instead - Test the command in your terminal first to rule out environment and PATH issues

