{"record":{"id":"05758ead7722b21a","repo":"HKUDS/Vibe-Trading","slug":"str-exc","errorCode":null,"errorMessage":"str(exc)","messagePattern":"str\\(exc\\)","errorType":"http","errorClass":"HTTPException","httpStatus":400,"severity":"error","filePath":"agent/src/api/sessions_routes.py","lineNumber":474,"sourceCode":"        if risk_tier is RiskTier.LIVE_TRADING_OR_EXECUTION:\n            raise HTTPException(status_code=400, detail=\"live trading or execution goals are not supported\")\n\n        goal_store = _get_goal_store()\n        try:\n            goal = goal_store.replace_goal(\n                session_id=session_id,\n                objective=req.objective,\n                criteria=criteria,\n                ui_summary=req.ui_summary,\n                source=\"api\",\n                protocol=req.protocol,\n                risk_tier=risk_tier,\n                token_budget=req.token_budget,\n                turn_budget=req.turn_budget,\n                time_budget_seconds=req.time_budget_seconds,\n            )\n        except ValueError as exc:\n            raise HTTPException(status_code=400, detail=str(exc)) from exc\n        snapshot = goal_store.get_goal_snapshot(goal.goal_id)\n        if snapshot is None:\n            raise HTTPException(status_code=500, detail=\"Goal created but could not be reloaded\")\n        svc.event_bus.emit(session_id, \"goal.created\", {\"goal\": snapshot[\"goal\"]})\n        return snapshot\n\n    @app.get(\n        \"/sessions/{session_id}/goal\",\n        response_model=GoalSnapshotResponse,\n        dependencies=[Depends(require_auth)],\n    )\n    async def get_session_goal(session_id: str):\n        \"\"\"Return the current finance research goal snapshot for a session.\"\"\"\n        _host_validate_path_param(session_id, \"session_id\")\n        _get_existing_session_or_404(session_id)\n        snapshot = _get_goal_store().get_current_snapshot(session_id)\n        if snapshot is None:\n            raise HTTPException(status_code=404, detail=\"No current goal\")","sourceCodeStart":456,"sourceCodeEnd":492,"githubUrl":"https://github.com/HKUDS/Vibe-Trading/blob/80ffdda44c5c4db0dd84d70e051cca591cea67df/agent/src/api/sessions_routes.py#L456-L492","documentation":"After enum checks, goal_store.replace_goal can still raise ValueError for business-rule violations (e.g. non-positive token_budget/turn_budget, contradictory time_budget_seconds, or invalid objective fields); the route converts that ValueError into 400 with the exception's own message as detail. The detail string is whatever the goal store validated, so read it literally.","triggerScenarios":"POST /sessions/{id}/goal with negative or zero budgets (token_budget<=0, turn_budget<=0, time_budget_seconds<=0), inconsistent budget combinations, or other values rejected by the goal store's validators after the risk-tier checks pass.","commonSituations":"Clients copying a config template whose budgets are unset and default to 0 or -1; UI allowing empty numeric fields serialized as 0; reducing budgets during an update below the goal's already-consumed amounts; unit drifts (minutes vs seconds).","solutions":["Read the 400 detail — it names the exact field and constraint that failed","Send strictly positive budgets (token_budget>0, turn_budget>0, time_budget_seconds>0) or omit them to use defaults","Pre-validate budget fields client-side before the call","Check for unit mistakes, especially seconds vs minutes for time budgets"],"exampleFix":"# before\napi.create_session_goal(sid, objective=\"o\", risk_tier=\"LOW_RISK\", token_budget=0, turn_budget=-1)  # 400 ValueError\n\n# after\napi.create_session_goal(sid, objective=\"o\", risk_tier=\"LOW_RISK\", token_budget=10000, turn_budget=20, time_budget_seconds=1800)","handlingStrategy":"validation","validationCode":"def validate_goal_payload(p: dict) -> None:\n    for f in (\"token_budget\", \"turn_budget\", \"time_budget_seconds\"):\n        if f in p and p[f] is not None and p[f] <= 0:\n            raise ValueError(f\"{f} must be > 0\")\n    if not (p.get(\"objective\") or \"\").strip():\n        raise ValueError(\"objective must be non-empty\")","typeGuard":"def is_valid_budget(v) -> TypeGuard[int]:\n    return isinstance(v, int) and not isinstance(v, bool) and v > 0","tryCatchPattern":"try:\n    goal = api.create_session_goal(sid, payload)\nexcept HTTPError as e:\n    if e.response.status_code == 400:\n        # detail is the goal store's own ValueError message — surface it verbatim\n        raise ValueError(f\"Goal rejected: {e.response.json()['detail']}\") from e\n    raise","preventionTips":["Validate budgets positive and objective non-empty client-side","Read the 400 detail literally — it names the offending field","Omit optional budgets instead of sending 0/placeholder values"],"tags":["validation","http-400","goals","budgets","value-error"],"backgroundTag":"schema-validation-failed","analyzedSha":"80ffdda44c5c4db0dd84d70e051cca591cea67df","analyzedAt":"2026-08-28T12:46:38.989Z","schemaVersion":2},"datasetVersion":"2026-08-28T16:17:29.566Z"}