{"record":{"id":"2af74558891713f5","repo":"pypa/pip","slug":"including-subprocess-data-sources-from-specific-ro","errorCode":null,"errorMessage":"Including subprocess data sources from specific root_dir is disallowed to prevent false information","messagePattern":"Including subprocess data sources from specific root_dir is disallowed to prevent false information","errorType":"exception","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"src/pip/_vendor/distro/distro.py","lineNumber":786,"sourceCode":"            etc_dir_os_release_file = os.path.join(self.etc_dir, _OS_RELEASE_BASENAME)\n            usr_lib_os_release_file = os.path.join(\n                self.usr_lib_dir, _OS_RELEASE_BASENAME\n            )\n\n            # NOTE: The idea is to respect order **and** have it set\n            #       at all times for API backwards compatibility.\n            if os.path.isfile(etc_dir_os_release_file) or not os.path.isfile(\n                usr_lib_os_release_file\n            ):\n                self.os_release_file = etc_dir_os_release_file\n            else:\n                self.os_release_file = usr_lib_os_release_file\n\n        self.distro_release_file = distro_release_file or \"\"  # updated later\n\n        is_root_dir_defined = root_dir is not None\n        if is_root_dir_defined and (include_lsb or include_uname or include_oslevel):\n            raise ValueError(\n                \"Including subprocess data sources from specific root_dir is disallowed\"\n                \" to prevent false information\"\n            )\n        self.include_lsb = (\n            include_lsb if include_lsb is not None else not is_root_dir_defined\n        )\n        self.include_uname = (\n            include_uname if include_uname is not None else not is_root_dir_defined\n        )\n        self.include_oslevel = (\n            include_oslevel if include_oslevel is not None else not is_root_dir_defined\n        )\n\n    def __repr__(self) -> str:\n        \"\"\"Return repr of all info\"\"\"\n        return (\n            \"LinuxDistribution(\"\n            \"os_release_file={self.os_release_file!r}, \"","sourceCodeStart":768,"sourceCodeEnd":804,"githubUrl":"https://github.com/pypa/pip/blob/f399c3718970b1b0e2478dac5296eb62679a9b86/src/pip/_vendor/distro/distro.py#L768-L804","documentation":"Raised by distro.LinuxDistribution.__init__ when the caller passes both root_dir (a custom filesystem root to read os-release / distro-release files from) and at least one subprocess-based data source flag (include_lsb, include_uname, or include_oslevel). The library forbids this combination because lsb_release, uname, and oslevel run as live subprocesses against the host system and cannot be scoped to an alternate root_dir, so mixing them would silently produce data from two different systems.","triggerScenarios":"Constructing LinuxDistribution(root_dir='/chroot/path', include_lsb=True) or any combination where root_dir is set and include_lsb/include_uname/include_oslevel is explicitly True.","commonSituations":"Inspecting a mounted disk image or chroot for its OS identity while also wanting uname/lsb_release data; copying example code that set include_lsb=True and then adding root_dir; library wrapper that unconditionally passes both.","solutions":["Set include_lsb=False, include_uname=False, include_oslevel=False when passing root_dir, and rely only on the os-release / distro-release files inside that root.","If you need uname/lsb_release data, call distro without root_dir so all sources come from the live system consistently.","Run lsb_release/uname manually against the target environment and merge results yourself."],"exampleFix":"# before\ninfo = LinuxDistribution(root_dir='/mnt/sysroot', include_lsb=True)\n\n# after\ninfo = LinuxDistribution(root_dir='/mnt/sysroot', include_lsb=False, include_uname=False)","handlingStrategy":"validation","validationCode":"def make_linux_distribution(root_dir=None, include_lsb=None, include_uname=None, include_oslevel=None):\n    if root_dir is not None and (include_lsb or include_uname or include_oslevel):\n        raise ValueError('Cannot combine root_dir with subprocess data sources')\n    import distro\n    return distro.LinuxDistribution(\n        root_dir=root_dir,\n        include_lsb=include_lsb if root_dir is None else False,\n        include_uname=include_uname if root_dir is None else False,\n        include_oslevel=include_oslevel if root_dir is None else False,\n    )","typeGuard":null,"tryCatchPattern":"try:\n    info = LinuxDistribution(root_dir=path, include_lsb=True)\nexcept ValueError as e:\n    if 'root_dir' in str(e):\n        info = LinuxDistribution(root_dir=path, include_lsb=False)\n    else:\n        raise","preventionTips":["When inspecting a chroot/image, always set all include_* flags to False.","Document that root_dir and subprocess sources are mutually exclusive.","Wrap LinuxDistribution construction in a helper that enforces the constraint."],"tags":["distro","configuration","validation","subprocess"],"backgroundTag":null,"analyzedSha":"f399c3718970b1b0e2478dac5296eb62679a9b86","analyzedAt":"2026-08-08T23:01:42.227Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}