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

  1. Ensure the value is a string, ReasoningEffort, or None before calling; extract nested fields first (payload['effort'] not payload).
  2. Type the field as ReasoningEffort | str | None in request models so validation happens at parse time.
  3. 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

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


AI-assisted analysis of zylon-ai/private-gpt@4a030776a3 (2026-08-15). Data as JSON: /api/errors/2c29d58ffaab0b5b. Report an issue: GitHub.