pytest-dev/pytest · error · ValueError

{self} is the upper-most scope

Error message

{self} is the upper-most scope

What it means

Mirror of the lower-most check: Scope.next_higher() walks one step up the Function < Class < Module < Package < Session chain. Session is the highest member (last index), so next_higher() on it has no target and raises ValueError. Like its sibling, this guards internal scope-walking logic and indicates a programming error, not a user configuration problem.

Source

Thrown at src/_pytest/scope.py:53

    # Scopes need to be listed from lower to higher.
    Function = "function"
    Class = "class"
    Module = "module"
    Package = "package"
    Session = "session"

    def next_lower(self) -> Scope:
        """Return the next lower scope."""
        index = _SCOPE_INDICES[self]
        if index == 0:
            raise ValueError(f"{self} is the lower-most scope")
        return _ALL_SCOPES[index - 1]

    def next_higher(self) -> Scope:
        """Return the next higher scope."""
        index = _SCOPE_INDICES[self]
        if index == len(_SCOPE_INDICES) - 1:
            raise ValueError(f"{self} is the upper-most scope")
        return _ALL_SCOPES[index + 1]

    def __lt__(self, other: Scope) -> bool:
        self_index = _SCOPE_INDICES[self]
        other_index = _SCOPE_INDICES[other]
        return self_index < other_index

    @classmethod
    def from_user(
        cls, scope_name: ScopeName, descr: str, where: str | None = None
    ) -> Scope:
        """
        Given a scope name from the user, return the equivalent Scope enum. Should be used
        whenever we want to convert a user provided scope name to its enum object.

        If the scope name is invalid, construct a user friendly message and call pytest.fail.
        """
        from _pytest.outcomes import fail

View on GitHub (pinned to 0d6fbdeffa)

Solutions

  1. Guard the call: check `scope is not Scope.Session` before invoking next_higher().
  2. Use `index < len(_SCOPE_INDICES) - 1` as the bound, mirroring the source check at scope.py:52.
  3. Prefer iterating _ALL_SCOPES explicitly with a bounded loop rather than relying on next_higher() to stop.

Example fix

# before
higher = scope.next_higher()

# after
if scope is Scope.Session:
    higher = None
else:
    higher = scope.next_higher()
Defensive patterns

Strategy: validation

Validate before calling

from _pytest.scope import Scope, _ALL_SCOPES, _SCOPE_INDICES

def safe_next_higher(scope: Scope):
    if scope is _ALL_SCOPES[-1]:
        return None
    return scope.next_higher()

Type guard

from _pytest.scope import Scope, _ALL_SCOPES

def has_higher_scope(scope: Scope) -> bool:
    """True when next_higher() is safe to call."""
    return scope is not _ALL_SCOPES[-1]

Try / catch

try:
    higher = scope.next_higher()
except ValueError:
    # scope is Session; nothing above it
    higher = None

Prevention

When it happens

Trigger: Calling Scope.Session.next_higher(), or any loop/helper that walks _ALL_SCOPES upward from Session without a bound. Produced by code paths in fixtures or plugins that ask for the next-higher scope, never by ini/CLI input.

Common situations: Plugin code that computes the enclosing scope of a session-scoped fixture; a refactor that walks scopes upward without checking for the top; tests that exercise Scope directly.

Related errors


AI-assisted analysis of pytest-dev/pytest@0d6fbdeffa (2026-08-11). Data as JSON: /api/errors/46c3886d40c2f0bb. Report an issue: GitHub.