zylon-ai/private-gpt · error · ValueError
Invalid reasoning_effort budget: {budget}. Must be a number
Error message
Invalid reasoning_effort budget: {budget}. Must be a number (int or float). What it means
Raised by _extract_budget in the reasoning_budget decorator when the reasoning effort budget kwarg is present but not an int or float (bool excluded in practice by type checks being strict on numeric types via isinstance(budget, int | float)). The decorator strips the budget kwarg from forwarded kwargs and converts it to int; a non-numeric value (string, None-handled separately, dict, etc.) cannot be converted safely, so it fails fast before the LLM call.
Source
Thrown at private_gpt/components/llm/decorators/reasoning_budget.py:75
merged_kwargs = dict(phase2.additional_kwargs)
for key in _USAGE_KEYS:
merged_kwargs[key] = _resolve_usage(phase1, key) + _resolve_usage(phase2, key)
return ChatResponse(
message=ChatMessage(
role=phase2.message.role,
content=phase2.message.content,
additional_kwargs=merged_message_kwargs,
),
additional_kwargs=merged_kwargs,
)
def _extract_budget(**kwargs: Any) -> tuple[int | None, dict[str, Any]]:
budget = kwargs.get(_REASONING_BUDGET_KWARG)
if budget is not None and not isinstance(budget, int | float):
raise ValueError(
f"Invalid reasoning_effort budget: {budget}. Must be a number (int or float)."
)
clean = {k: v for k, v in kwargs.items() if k != _REASONING_BUDGET_KWARG}
return (int(budget) if budget is not None else None), clean
def _build_continue_messages(
messages: Sequence[ChatMessage],
phase1: ChatResponse,
) -> list[ChatMessage]:
return [*messages, phase1.message]
def _effective_reasoning(
reasoning_effort: ReasoningEffort,
budget: int | None,
) -> ReasoningEffort:View on GitHub (pinned to 4a030776a3)
Solutions
- Coerce before calling: pass int(...) or float(...) for the budget kwarg.
- Fix the config source: unquote the number in YAML/env-derived settings so it loads as a numeric type.
- If the value comes from user input, validate/convert it at the boundary (Pydantic field with int|float type).
Example fix
# before await llm.achat(messages, reasoning_budget="8192") # after await llm.achat(messages, reasoning_budget=8192)
Defensive patterns
Strategy: validation
Validate before calling
def valid_budget(value: object) -> bool:
return value is None or (isinstance(value, (int, float)) and not isinstance(value, bool)) Type guard
def is_numeric_budget(value: object) -> bool:
return value is None or (isinstance(value, (int, float)) and not isinstance(value, bool)) Try / catch
try:
await llm.achat(messages, reasoning_budget=budget)
except ValueError as e:
if 'reasoning_effort budget' in str(e) and isinstance(budget, str):
await llm.achat(messages, reasoning_budget=int(budget))
else:
raise Prevention
- Declare numeric config fields with strict types (pydantic int/float) so strings never reach the call.
- Convert CLI/env inputs with int()/float() at the boundary, not at the LLM call site.
- Never forward untyped dict kwargs from user payloads directly into decorated LLM methods.
When it happens
Trigger: Passing reasoning budget as a kwarg to a decorated chat call with a wrong type, e.g. budget="8000" (string), budget=[4096] (list), or budget=True where the code path rejects it. Any call site reading budget from config/env as a string and forwarding it unchanged will hit this.
Common situations: YAML/env config storing the budget as a string ("reasoning_budget: '4096'") and being forwarded verbatim; CLI arguments arriving as strings; serialized JSON settings where numbers were quoted.
Related errors
- Audio blocks found but no audio-capable LLM provided.
- Configured model does not support function calling
- REQUEST_TOO_LARGE_ERROR
- LLM mode '{mode}' is not supported. Available: {available}
- Default LLM model '{model_id}' could not be initialized: {e}
AI-assisted analysis of zylon-ai/private-gpt@4a030776a3 (2026-08-15).
Data as JSON: /api/errors/2a68c11e98b03b9f.
Report an issue: GitHub.