You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
subprocess: CalledProcessError.__str__ crashes when returncode is None #153970
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.
importsubprocesserr=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.
@picnixz Fair point, and you're right that the constructor isn't documented, and that subprocess itself always builds this with a real integer return code.
The reasoning is less "people construct this directly" and more that an exception's __str__ shouldn't itself raise. CalledProcessError and its returncode attribute are public and documented, and exceptions do get constructed and re-raised by test code and by third-party libraries that imitate subprocess. If one of those ends up with returncode=None and propagates, the traceback machinery calls str() on it and you get a confusing chained TypeError that hides the real error instead of a readable message.
I'm still unsure about this. In this case, I would rather leave it as is because it's already documented as being an exit code, so an integer. We don't do overly defensive programming in the stdlib.
I personally think it's not relevant here. We can improve the documentation though, but I don't see a reason for raising a ValueError in __str__ for None exit codes. If such code can be legitimately obtained by the public API, then we ought to fix it, sure. But such fix should not raise a ValueError either and rather present a different __str__.
If one of those ends up with returncode=None and propagates, the traceback machinery calls str() on it and you get a confusing chained TypeError that hides the real error instead of a readable message.
FTR, we would still consider a GIGO case. The exception would still point at the __str__ method so it wouldn't help for much more for debugging either.
After looking at Victor's suggestion on the PR, I think we can just use %s instead of %d (or an f-string) for the return code. It would make it more friendly while allowing arbitrary types.
It needs a backport to 3.15 (which may be blocked by other backports).
Yes. I already had this PR in my "3.15 backport queue". The PR cannot be created just with git cherry-pick -x 7c653e2540cbe4180efb3bd83b63865a1f675650, other changes should be backported to 3.15 first.
Activity
picnixz commentedon Jul 18, 2026
I'm not entirely sure you're meant to create
CalledProcessErroryourself. The docs say:So I don't thinkt he constructor is meant to be part of the public API. Note that the constructor is not documented either.
fedonman commentedon Jul 18, 2026
@picnixz Fair point, and you're right that the constructor isn't documented, and that subprocess itself always builds this with a real integer return code.
The reasoning is less "people construct this directly" and more that an exception's
__str__shouldn't itself raise.CalledProcessErrorand itsreturncodeattribute are public and documented, and exceptions do get constructed and re-raised by test code and by third-party libraries that imitatesubprocess. If one of those ends up withreturncode=Noneand propagates, the traceback machinery callsstr()on it and you get a confusing chainedTypeErrorthat hides the real error instead of a readable message.devdanzin commentedon Jul 18, 2026
Me neither, but if someone is creating a new subclass they could benefit from not having an admittedly hard to hit footgun. E.g.:
picnixz commentedon Jul 18, 2026
I'm still unsure about this. In this case, I would rather leave it as is because it's already documented as being an exit code, so an integer. We don't do overly defensive programming in the stdlib.
fedonman commentedon Jul 18, 2026
Should I close the issue?
picnixz commentedon Jul 18, 2026
I personally think it's not relevant here. We can improve the documentation though, but I don't see a reason for raising a
ValueErrorin__str__for None exit codes. If such code can be legitimately obtained by the public API, then we ought to fix it, sure. But such fix should not raise aValueErroreither and rather present a different__str__.cc @gpshead
picnixz commentedon Jul 18, 2026
FTR, we would still consider a GIGO case. The exception would still point at the
__str__method so it wouldn't help for much more for debugging either.picnixz commentedon Jul 18, 2026
After looking at Victor's suggestion on the PR, I think we can just use
%sinstead of%d(or an f-string) for the return code. It would make it more friendly while allowing arbitrary types.Merge branch 'main' into fix-pythongh-153970-calledprocesserror-none
5 remaining items
pythongh-153970: Validate the returncode type in CalledProcessError
Merge branch 'main' into fix-pythongh-153970-calledprocesserror-none
Merge branch 'main' into fix-pythongh-153970-calledprocesserror-none
pythongh-153970: Make str() of CalledProcessError work for any return…
gh-153970: Fix str() of CalledProcessError when returncode is not an …
pythongh-153970: Fix str() of CalledProcessError when returncode is n…
[3.14] gh-153970: Fix str() of CalledProcessError when returncode is …
serhiy-storchaka commentedon Aug 21, 2026
It needs a backport to 3.15 (which may be blocked by other backports).
vstinner commentedon Aug 31, 2026
Yes. I already had this PR in my "3.15 backport queue". The PR cannot be created just with
git cherry-pick -x 7c653e2540cbe4180efb3bd83b63865a1f675650, other changes should be backported to 3.15 first.[3.13] gh-153970: Fix str() of CalledProcessError when returncode is …
[3.15] gh-153970: Fix str() of CalledProcessError when returncode is …
vstinner commentedon Oct 9, 2026
I just merged the 3.15 backport. Thanks for the reminder!