pytest-dev/pytest · error · ValueError
{self} is the lower-most scope
Error message
{self} is the lower-most scope What it means
pytest models fixture lifetimes as an ordered Scope enum: Function < Class < Module < Package < Session. Scope.next_lower() walks one step down that chain. Function is at index 0, so it has no lower neighbor and the call refuses rather than return a sentinel. The error is raised from internal scope-walking code, not from user-facing config parsing, so it signals a programming bug in pytest internals or a plugin that walks scopes without a bounds check.
Source
Thrown at src/_pytest/scope.py:46
->>> higher ->>>
Function < Class < Module < Package < Session
<<<- lower <<<-
"""
# 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:View on GitHub (pinned to 0d6fbdeffa)
Solutions
- Guard the call: check `scope is not Scope.Function` (or `scope != _ALL_SCOPES[0]`) before invoking next_lower().
- Iterate the module-level HIGH_SCOPES list (all scopes except Function) instead of walking next_lower() from the bottom.
- If you genuinely need a sentinel below Function, model it as a separate value rather than relying on next_lower() to produce one.
Example fix
# before
lower = scope.next_lower()
# after
if scope is Scope.Function:
lower = None
else:
lower = scope.next_lower() Defensive patterns
Strategy: validation
Validate before calling
from _pytest.scope import Scope, _ALL_SCOPES
def safe_next_lower(scope: Scope):
# Validate before calling next_lower() to avoid ValueError on Function.
if scope is _ALL_SCOPES[0]:
return None
return scope.next_lower() Type guard
from _pytest.scope import Scope, _ALL_SCOPES
def has_lower_scope(scope: Scope) -> bool:
"""True when next_lower() is safe to call."""
return scope is not _ALL_SCOPES[0] Try / catch
try:
lower = scope.next_lower()
except ValueError:
# scope is Function; nothing below it
lower = None Prevention
- Treat next_lower() as partial: always check against the lowest scope first.
- Prefer iterating the module-level _ALL_SCOPES/HIGH_SCOPES lists over walking next_lower().
- In plugin code, never assume a caller-provided scope has a lower neighbor.
When it happens
Trigger: Calling Scope.Function.next_lower() directly, or any helper that iterates _ALL_SCOPES downward starting from Function. Reachable only via code that holds a Scope enum member and asks for the next-lower one; not produced by ini config or CLI flags.
Common situations: Plugin authors implementing fixture finalization that walks scopes; porting an old pytest fork whose scope list differed; an internal refactor that drops the bounds check before calling next_lower(). End users running stock pytest effectively never see this.
Related errors
- {self} is the upper-most scope
- function not available in {self.scope}-scoped context
- cls not available in {self.scope}-scoped context
- module not available in {self.scope}-scoped context
- path not available in {self.scope}-scoped context
AI-assisted analysis of pytest-dev/pytest@0d6fbdeffa (2026-08-11).
Data as JSON: /api/errors/e440ec4c86288baa.
Report an issue: GitHub.