{"record":{"id":"cbc28065ec305fbf","repo":"HKUDS/Vibe-Trading","slug":"name-must-be-positive","errorCode":null,"errorMessage":"{name} must be positive","messagePattern":"(.+?) must be positive","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"agent/src/goal/store.py","lineNumber":312,"sourceCode":"        \"\"\"\n        session_id = normalize_required_text(session_id, \"session_id\")\n        objective = normalize_required_text(objective, \"goal objective\")\n        reject_live_execution_objective(objective)\n        if risk_tier is RiskTier.LIVE_TRADING_OR_EXECUTION:\n            raise ValueError(\"live trading or execution goals are not supported\")\n        cleaned_criteria = [item.strip() for item in criteria if item.strip()]\n        if not cleaned_criteria:\n            raise ValueError(\"at least one goal criterion is required\")\n        for criterion in cleaned_criteria:\n            reject_live_execution_objective(criterion)\n        budgets = {\n            \"token_budget\": token_budget,\n            \"turn_budget\": turn_budget,\n            \"time_budget_seconds\": time_budget_seconds,\n        }\n        for name, value in budgets.items():\n            if value is not None and value <= 0:\n                raise ValueError(f\"{name} must be positive\")\n\n        now = _now_iso()\n        goal_id = _id(\"goal\")\n        summary = ui_summary.strip() or objective[:80]\n        current_values = [status.value for status in _CURRENT_STATUSES]\n        placeholders = \",\".join(\"?\" for _ in current_values)\n\n        with self._write_transaction():\n            self._conn.execute(\n                f\"\"\"\n                UPDATE goals\n                SET status = ?, updated_at = ?, completed_at = COALESCE(completed_at, ?)\n                WHERE session_id = ? AND status IN ({placeholders})\n                \"\"\",\n                [GoalStatus.SUPERSEDED.value, now, now, session_id, *current_values],\n            )\n            self._conn.execute(\n                \"\"\"","sourceCodeStart":294,"sourceCodeEnd":330,"githubUrl":"https://github.com/HKUDS/Vibe-Trading/blob/80ffdda44c5c4db0dd84d70e051cca591cea67df/agent/src/goal/store.py#L294-L330","documentation":"replace_goal validates the three budget fields it stores (token_budget, turn_budget, time_budget_seconds): any non-None value <= 0 raises ValueError('{name} must be positive'). Zero or negative budgets are meaningless as limits, so they are rejected before the goal row is written.","triggerScenarios":"Calling replace_goal with token_budget=0, turn_budget=-1, or time_budget_seconds=0 (None is allowed and skips the check). Often happens when a caller computes a budget that floors to 0, or copies a default of 0 from config.","commonSituations":"Config defaults of 0 meaning 'unlimited' — this API uses None for unlimited; arithmetic like max(0, remaining) before the call; UI number inputs defaulting to 0; env vars parsed as 0 when unset.","solutions":["Pass None instead of 0 to mean 'no budget limit'","Compute budgets with max(1, ...) when a small-but-set limit is intended","Validate config at load: coerce 0 to None or fail with a clear message naming the field","Check which of the three budgets is zero in the error message and fix that caller path"],"exampleFix":"# before\nstore.replace_goal(session_id=\"s1\", objective=obj, criteria=c, token_budget=0, turn_budget=5)\n# after\nstore.replace_goal(session_id=\"s1\", objective=obj, criteria=c, token_budget=None, turn_budget=5)","handlingStrategy":"validation","validationCode":"def clean_budget(v):\n    return None if v is None or v <= 0 else int(v)\n\nstore.replace_goal(\n    session_id=sid, objective=obj, criteria=cs,\n    token_budget=clean_budget(token_budget),\n    turn_budget=clean_budget(turn_budget),\n    time_budget_seconds=clean_budget(time_budget_seconds),\n)","typeGuard":"def is_valid_budget(v) -> bool:\n    return v is None or (isinstance(v, int) and not isinstance(v, bool) and v > 0)","tryCatchPattern":"try:\n    store.replace_goal(session_id=sid, objective=obj, criteria=cs, token_budget=tb)\nexcept ValueError as e:\n    if 'must be positive' in str(e):\n        tb = None  # interpret 0 as unlimited\n        store.replace_goal(session_id=sid, objective=obj, criteria=cs, token_budget=tb)\n    else:\n        raise","preventionTips":["Use None for 'no budget', never 0","Coerce config zeros to None at load time","Use max(1, x) when a real small limit is intended"],"tags":["validation","goal-store","budget","positive-integer"],"backgroundTag":"non-positive-numeric-argument","analyzedSha":"80ffdda44c5c4db0dd84d70e051cca591cea67df","analyzedAt":"2026-08-28T12:46:38.989Z","schemaVersion":2},"datasetVersion":"2026-08-28T16:17:29.566Z"}