{"record":{"id":"ce0c1beae3e81d5e","repo":"pypa/pip","slug":"req-path-recursively-references-itself-in-filen","errorCode":null,"errorMessage":"{req_path} recursively references itself in {filename}{tail}","messagePattern":"(.+?) recursively references itself in (.+?)(.+?)","errorType":"validation","errorClass":"RequirementsFileParseError","httpStatus":null,"severity":"error","filePath":"src/pip/_internal/req/req_file.py","lineNumber":390,"sourceCode":"                # original file and nested file are paths\n                elif not SCHEME_RE.search(req_path):\n                    # do a join so relative paths work\n                    # and then abspath so that we can identify recursive references\n                    req_path = os.path.abspath(\n                        os.path.join(\n                            os.path.dirname(filename),\n                            req_path,\n                        )\n                    )\n                parsed_files = parsed_files_stack[0]\n                if req_path in parsed_files:\n                    initial_file = parsed_files[req_path]\n                    tail = (\n                        f\" and again in {initial_file}\"\n                        if initial_file is not None\n                        else \"\"\n                    )\n                    raise RequirementsFileParseError(\n                        f\"{req_path} recursively references itself in {filename}{tail}\"\n                    )\n                # Keeping a track where was each file first included in\n                new_parsed_files = parsed_files.copy()\n                new_parsed_files[req_path] = filename\n                yield from self._parse_and_recurse(\n                    req_path, nested_constraint, [new_parsed_files, *parsed_files_stack]\n                )\n            else:\n                yield line\n\n    def _parse_file(\n        self, filename: str, constraint: bool\n    ) -> Generator[ParsedLine, None, None]:\n        _, content = get_file_content(filename, self._session, constraint=constraint)\n\n        lines_enum = preprocess(content)\n","sourceCodeStart":372,"sourceCodeEnd":408,"githubUrl":"https://github.com/pypa/pip/blob/f399c3718970b1b0e2478dac5296eb62679a9b86/src/pip/_internal/req/req_file.py#L372-L408","documentation":"A requirements file (-r) or constraint file (-c) includes another file that, directly or transitively, includes the original file again, creating a cycle. pip tracks all files in the current parse stack (parsed_files_stack) and raises RequirementsFileParseError when it detects the same absolute path reappearing.","triggerScenarios":"requirements.txt has '-r other.txt' and other.txt has '-r requirements.txt'. Or a single file contains '-r requirements.txt' referencing itself. The check 'if req_path in parsed_files' catches it.","commonSituations":"Refactoring requirements into shared files and accidentally creating circular includes. Symlinks that cause path aliasing. Generated requirements files that reference their generator.","solutions":["Trace the include chain from the error message (it names the files involved) and break the cycle","Restructure requirements files into a non-circular hierarchy: a base file included by leaf files","If you have shared constraints, put them in a single file that no other file includes back","Use 'readlink -f' to detect symlink-induced cycles that create different paths to the same file"],"exampleFix":"# before — requirements.txt:\n-r other.txt\n# other.txt:\n-r requirements.txt   # cycle!\n\n# after — requirements.txt:\n-r base.txt\n-r app.txt\n# base.txt and app.txt do NOT include requirements.txt","handlingStrategy":"validation","validationCode":"import os\n\ndef find_circular_includes(filename: str, _seen: set | None = None) -> str | None:\n    \"\"\"Returns the path of a circular reference, or None.\"\"\"\n    if _seen is None:\n        _seen = set()\n    abspath = os.path.abspath(filename)\n    if abspath in _seen:\n        return abspath\n    _seen.add(abspath)\n    try:\n        with open(abspath) as f:\n            for line in f:\n                line = line.strip()\n                if line.startswith(\"-r \") or line.startswith(\"-c \"):\n                    nested = line.split(None, 1)[1]\n                    nested_path = os.path.join(os.path.dirname(abspath), nested)\n                    result = find_circular_includes(nested_path, _seen.copy())\n                    if result:\n                        return result\n    except OSError:\n        pass\n    return None","typeGuard":"import os\n\ndef has_no_circular_includes(filename: str) -> bool:\n    \"\"\"Type guard: True if the requirements file hierarchy is acyclic.\"\"\"\n    return find_circular_includes(filename) is None","tryCatchPattern":"from pip._internal.exceptions import RequirementsFileParseError\nfrom pip._internal.req.req_file import parse_requirements\n\ntry:\n    list(parse_requirements(\"requirements.txt\", session))\nexcept RequirementsFileParseError as e:\n    if \"recursively references itself\" in str(e):\n        print(\"Break the circular -r/-c include chain\")\n    else:\n        raise","preventionTips":["Design requirements files as a DAG: base files included by leaf files, never circular","Use a validation script to detect circular includes before running pip","Avoid symlinks in requirements file directories that could alias the same file"],"tags":["pip","requirements-file","circular-reference","recursion","parsing"],"backgroundTag":null,"analyzedSha":"f399c3718970b1b0e2478dac5296eb62679a9b86","analyzedAt":"2026-08-08T23:01:42.227Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}