{"id":"6330065c40be809a","repo":"pypa/pip","slug":"invalid-value-on-returncode-on-returncode-r","errorCode":null,"errorMessage":"Invalid value: on_returncode={on_returncode!r}","messagePattern":"Invalid value: on_returncode=(.+?)","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"src/pip/_internal/utils/subprocess.py","lineNumber":224,"sourceCode":"                )\n                subprocess_logger.verbose(\n                    \"[bold magenta]cwd[/]: %s\",\n                    escape(cwd or \"[inherit]\"),\n                    extra={\"markup\": True},\n                )\n\n            raise error\n        elif on_returncode == \"warn\":\n            subprocess_logger.warning(\n                'Command \"%s\" had error code %s in %s',\n                command_desc,\n                proc.returncode,\n                cwd,\n            )\n        elif on_returncode == \"ignore\":\n            pass\n        else:\n            raise ValueError(f\"Invalid value: on_returncode={on_returncode!r}\")\n    return output\n\n\ndef runner_with_spinner_message(message: str) -> Callable[..., None]:\n    \"\"\"Provide a subprocess_runner that shows a spinner message.\n\n    Intended for use with for BuildBackendHookCaller. Thus, the runner has\n    an API that matches what's expected by BuildBackendHookCaller.subprocess_runner.\n    \"\"\"\n\n    def runner(\n        cmd: list[str],\n        cwd: str | None = None,\n        extra_environ: Mapping[str, Any] | None = None,\n    ) -> None:\n        with open_spinner(message) as spinner:\n            call_subprocess(\n                cmd,","sourceCodeStart":206,"sourceCodeEnd":242,"githubUrl":"https://github.com/pypa/pip/blob/d7d0d0a39494e28ec1c407bd0680e4a4d1067791/src/pip/_internal/utils/subprocess.py#L206-L242","documentation":"Defensive ValueError raised by pip's subprocess runner when the `on_returncode` argument is not one of the three allowed literals: 'raise', 'warn', or 'ignore'. It fires only when a subprocess the runner invoked actually exits non-zero and the supplied handling mode is unrecognized. The default is 'raise', so this only happens for callers passing a custom bad value.","triggerScenarios":"Calling pip's internal subprocess runner (e.g. via BuildBackendHookCaller or a custom call site) with on_returncode set to a typo like 'raises', 'abort', 'fail', None, or an unexpected Enum. Only triggers if the subprocess actually returns non-zero.","commonSituations":"Third-party code subclassing or reusing pip's runner with a typo; passing a value intended for a different pip version's API.","solutions":["Pass one of exactly 'raise', 'warn', or 'ignore' for on_returncode.","If you need custom handling, leave on_returncode='raise' and catch the resulting InstallationSubprocessError yourself.","Audit third-party plugins for typos in the on_returncode argument."],"exampleFix":"// before\nrunner.run(cmd, on_returncode=\"abort\")\n\n// after\nrunner.run(cmd, on_returncode=\"raise\")","handlingStrategy":"type-guard","validationCode":"ALLOWED = {'raise', 'warn', 'ignore'}\nassert on_returncode in ALLOWED, f'on_returncode must be one of {ALLOWED}'\nrunner.run(cmd, on_returncode=on_returncode)","typeGuard":"from typing import Literal\n\ndef is_valid_returncode(x: object) -> bool:\n    return x in ('raise', 'warn', 'ignore')\n\nValidReturnCode = Literal['raise', 'warn', 'ignore']","tryCatchPattern":"null","preventionTips":["Use a Literal type so static checkers reject typos.","Default to on_returncode='raise' and handle errors at the call site.","Audit third-party callers of pip's runner for the allowed values."],"tags":["pip","subprocess","internal-api","invalid-argument"],"analyzedSha":"d7d0d0a39494e28ec1c407bd0680e4a4d1067791","analyzedAt":"2026-08-04T20:55:04.259Z","schemaVersion":2}