{"record":{"id":"2a68c11e98b03b9f","repo":"zylon-ai/private-gpt","slug":"invalid-reasoning-effort-budget-budget-must-be","errorCode":null,"errorMessage":"Invalid reasoning_effort budget: {budget}. Must be a number (int or float).","messagePattern":"Invalid reasoning_effort budget: (.+?)\\. Must be a number \\(int or float\\)\\.","errorType":"exception","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"private_gpt/components/llm/decorators/reasoning_budget.py","lineNumber":75,"sourceCode":"\n    merged_kwargs = dict(phase2.additional_kwargs)\n    for key in _USAGE_KEYS:\n        merged_kwargs[key] = _resolve_usage(phase1, key) + _resolve_usage(phase2, key)\n\n    return ChatResponse(\n        message=ChatMessage(\n            role=phase2.message.role,\n            content=phase2.message.content,\n            additional_kwargs=merged_message_kwargs,\n        ),\n        additional_kwargs=merged_kwargs,\n    )\n\n\ndef _extract_budget(**kwargs: Any) -> tuple[int | None, dict[str, Any]]:\n    budget = kwargs.get(_REASONING_BUDGET_KWARG)\n    if budget is not None and not isinstance(budget, int | float):\n        raise ValueError(\n            f\"Invalid reasoning_effort budget: {budget}. Must be a number (int or float).\"\n        )\n\n    clean = {k: v for k, v in kwargs.items() if k != _REASONING_BUDGET_KWARG}\n    return (int(budget) if budget is not None else None), clean\n\n\ndef _build_continue_messages(\n    messages: Sequence[ChatMessage],\n    phase1: ChatResponse,\n) -> list[ChatMessage]:\n    return [*messages, phase1.message]\n\n\ndef _effective_reasoning(\n    reasoning_effort: ReasoningEffort,\n    budget: int | None,\n) -> ReasoningEffort:","sourceCodeStart":57,"sourceCodeEnd":93,"githubUrl":"https://github.com/zylon-ai/private-gpt/blob/4a030776a31a901ad80b1bf4d7faa2c1a367efbb/private_gpt/components/llm/decorators/reasoning_budget.py#L57-L93","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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)."],"exampleFix":"# before\nawait llm.achat(messages, reasoning_budget=\"8192\")\n\n# after\nawait llm.achat(messages, reasoning_budget=8192)","handlingStrategy":"validation","validationCode":"def valid_budget(value: object) -> bool:\n    return value is None or (isinstance(value, (int, float)) and not isinstance(value, bool))","typeGuard":"def is_numeric_budget(value: object) -> bool:\n    return value is None or (isinstance(value, (int, float)) and not isinstance(value, bool))","tryCatchPattern":"try:\n    await llm.achat(messages, reasoning_budget=budget)\nexcept ValueError as e:\n    if 'reasoning_effort budget' in str(e) and isinstance(budget, str):\n        await llm.achat(messages, reasoning_budget=int(budget))\n    else:\n        raise","preventionTips":["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."],"tags":["llm","reasoning","type-error","configuration"],"backgroundTag":null,"analyzedSha":"4a030776a31a901ad80b1bf4d7faa2c1a367efbb","analyzedAt":"2026-08-15T03:51:26.951Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}