pypa/pip · error · RequirementsFileParseError

{req_path} recursively references itself in {filename}{tail}

Error message

{req_path} recursively references itself in {filename}{tail}

What it means

RequirementsFileParseError raised when a requirements file referenced via '-r'/'--requirements' (or '-c' constraints) transitively includes itself, forming a cycle. pip tracks every file it has started parsing in parsed_files_stack and aborts as soon as a path reappears, to prevent infinite recursion. The message names the file and where it was first seen.

Source

Thrown at src/pip/_internal/req/req_file.py:390

                # original file and nested file are paths
                elif not SCHEME_RE.search(req_path):
                    # do a join so relative paths work
                    # and then abspath so that we can identify recursive references
                    req_path = os.path.abspath(
                        os.path.join(
                            os.path.dirname(filename),
                            req_path,
                        )
                    )
                parsed_files = parsed_files_stack[0]
                if req_path in parsed_files:
                    initial_file = parsed_files[req_path]
                    tail = (
                        f" and again in {initial_file}"
                        if initial_file is not None
                        else ""
                    )
                    raise RequirementsFileParseError(
                        f"{req_path} recursively references itself in {filename}{tail}"
                    )
                # Keeping a track where was each file first included in
                new_parsed_files = parsed_files.copy()
                new_parsed_files[req_path] = filename
                yield from self._parse_and_recurse(
                    req_path, nested_constraint, [new_parsed_files, *parsed_files_stack]
                )
            else:
                yield line

    def _parse_file(
        self, filename: str, constraint: bool
    ) -> Generator[ParsedLine, None, None]:
        _, content = get_file_content(filename, self._session, constraint=constraint)

        lines_enum = preprocess(content)

View on GitHub (pinned to d7d0d0a394)

Solutions

  1. Inspect each file named in the message and remove the '-r'/'-c' line that points back up the chain.
  2. If you need shared content, factor the common lines into a third file that both include, with nothing pointing back.
  3. Check for symlinks: 'ls -l <file>' and resolve with 'readlink -f' to confirm no loop.
  4. Re-run pip after editing to confirm the cycle is broken.

Example fix

# before — requirements/a.txt
-r requirements/b.txt
# requirements/b.txt
-r requirements/a.txt   # cycle

# after — factor out shared
# requirements/a.txt and requirements/b.txt both do:
-r requirements/_common.txt
Defensive patterns

Strategy: validation

Validate before calling

def detect_cycle(start: str) -> bool:
    import os
    seen, stack = set(), [os.path.abspath(start)]
    while stack:
        cur = stack.pop()
        if cur in seen:
            return True
        seen.add(cur)
        try:
            for line in open(cur):
                line = line.strip()
                if line.startswith(('-r ', '--requirements ', '-c ', '--constraint ')):
                    ref = line.split(maxsplit=1)[1]
                    if not os.path.isabs(ref):
                        ref = os.path.join(os.path.dirname(cur), ref)
                    stack.append(os.path.abspath(ref))
        except OSError:
            pass
    return False

Type guard

def is_acyclic_requirements_graph(root: str) -> bool:
    return not detect_cycle(root)

Try / catch

if detect_cycle('requirements.txt'):
    print('requirements cycle detected; edit files before pip')
else:
    run_pip(['install', '-r', 'requirements.txt'])

Prevention

When it happens

Trigger: a.txt contains '-r b.txt' and b.txt contains '-r a.txt'; or a.txt contains '-r a.txt' directly; or a self-referential symlink chain used as a requirements file. Triggered via 'pip install -r a.txt'.

Common situations: Refactoring a monorepo's shared requirements file and accidentally pointing a child back at the parent; symlink-based requirements files that loop; generated requirements files that emit their own filename.

Related errors


AI-assisted analysis of pypa/pip@d7d0d0a394 (2026-08-04). Data as JSON: /data/errors/ce0c1beae3e81d5e.json. Report an issue: GitHub.