zylon-ai/private-gpt · error · TypeError
reasoning_effort must be a ReasoningEffort, string, or None;
Error message
reasoning_effort must be a ReasoningEffort, string, or None; got {type(reasoning_effort).__name__} What it means
TypeError from normalize_reasoning_effort: the value crossing an untyped/serialized boundary is not None, not a ReasoningEffort, and not a str — e.g. an int, dict, or list. The function exists precisely to normalize values coming from serialized LLM payloads; only None/str/enum are acceptable, everything else is a type error rather than a coercion.
Source
Thrown at private_gpt/components/llm/models.py:34
return cls(effort_str)
raise ValueError(f"Unknown reasoning effort level: {effort_str}")
@property
def is_thinking_enabled(self) -> bool:
return self != ReasoningEffort.NONE
def normalize_reasoning_effort(
reasoning_effort: ReasoningEffort | str | None,
) -> ReasoningEffort:
"""Normalize values crossing untyped or serialized LLM boundaries."""
if reasoning_effort is None:
return ReasoningEffort.NONE
if isinstance(reasoning_effort, ReasoningEffort):
return reasoning_effort
if isinstance(reasoning_effort, str):
return ReasoningEffort.from_str(reasoning_effort)
raise TypeError(
"reasoning_effort must be a ReasoningEffort, string, or None; "
f"got {type(reasoning_effort).__name__}"
)
def _get_exception_types() -> tuple[type[BaseException], ...]:
base_exceptions = (ConnectionError, TimeoutError, OSError)
try:
from grpc.aio import AioRpcError # ty:ignore[unresolved-import]
from tritonclient.utils import ( # ty:ignore[unresolved-import]
InferenceServerException,
)
return *base_exceptions, AioRpcError, InferenceServerException
except ImportError:
return base_exceptions
View on GitHub (pinned to 4a030776a3)
Solutions
- Ensure the value is a string, ReasoningEffort, or None before calling; extract nested fields first (payload['effort'] not payload).
- Type the field as ReasoningEffort | str | None in request models so validation happens at parse time.
- For numeric codes, map them to the string levels before calling.
Example fix
# before
effort = normalize_reasoning_effort(request_body) # dict -> TypeError
# after
effort = normalize_reasoning_effort(request_body.get("effort")) Defensive patterns
Strategy: type-guard
Validate before calling
def is_normalizable_effort(value: object) -> bool:
return value is None or isinstance(value, (str, ReasoningEffort)) Type guard
from private_gpt.components.llm.models import ReasoningEffort
def is_normalizable_effort(value: object) -> bool:
return value is None or isinstance(value, (str, ReasoningEffort)) Try / catch
try:
effort = normalize_reasoning_effort(value)
except TypeError:
log.warning('Bad reasoning_effort type %s; defaulting to none', type(value).__name__)
effort = ReasoningEffort.NONE Prevention
- Type the field as ReasoningEffort | str | None in pydantic models crossing API boundaries.
- Extract nested fields (payload['effort']) before normalizing; never pass whole dicts.
- Add unit tests asserting TypeError for ints/dicts to lock in the contract.
When it happens
Trigger: Passing reasoning_effort as a non-string: an int (e.g. 2 from an API that numeric-codes effort), a dict from parsed JSON like {'effort': 'high'}, or a list. Happens when request payloads or DB rows are forwarded without field-level validation.
Common situations: REST handlers forwarding body fields verbatim; dataclass/pydantic models typing the field as Any; values deserialized from JSON where the effort was nested or numeric.
Related errors
- Invalid reasoning_effort budget: {budget}. Must be a number
- Unknown reasoning effort level: {effort_str}
- Invalid system specification (dict): {system}
- Invalid system item in list (dict): {item}
- 'oneOf' must be an array of schemas
AI-assisted analysis of zylon-ai/private-gpt@4a030776a3 (2026-08-15).
Data as JSON: /api/errors/2c29d58ffaab0b5b.
Report an issue: GitHub.