Calling str() on a CalledProcessError crashes if its returncode is None.
The __str__ method uses if self.returncode and self.returncode < 0 for the "died with a signal" case, and otherwise formats the return code with %d. When returncode is None, that first check is falsy, so it falls through to the %d branch, and %d can't format None, so it raises TypeError. Having the exception's own string representation blow up is a pretty bad way to fail.
import subprocess err = subprocess.CalledProcessError(None, "cmd") str(err) # TypeError: %d format: a real number is required, not NoneType
The fix is to check for returncode is None before the %d branch and return a plain message instead.
Found while going through devdanzin's audit of the standard library, item 9: https://gist.github.com/devdanzin/3198710e3c0128fda5e0a7b4e0768e5f
Linked PRs
- gh-153970: Fix str() of CalledProcessError when returncode is not an integer #153971
- [3.14] gh-153970: Fix str() of CalledProcessError when returncode is not an integer (GH-153971) #155923
- [3.14] gh-153970: Fix str() of CalledProcessError when returncode is not an integer (GH-153971) #155924
- [3.13] gh-153970: Fix str() of CalledProcessError when returncode is not an integer (GH-153971) #155931