{"record":{"id":"47e96bbdc074f04e","repo":"HKUDS/Vibe-Trading","slug":"usage-deltas-must-be-non-negative","errorCode":null,"errorMessage":"usage deltas must be non-negative","messagePattern":"usage deltas must be non-negative","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"agent/src/goal/store.py","lineNumber":783,"sourceCode":"        updated = self.get_goal(goal_id)\n        if updated is None:\n            raise RuntimeError(\"updated goal could not be reloaded\")\n        return updated\n\n    @_synchronized\n    def account_usage(\n        self,\n        *,\n        session_id: str,\n        goal_id: str,\n        expected_goal_id: str,\n        token_delta: int = 0,\n        time_delta_seconds: int = 0,\n        turn_delta: int = 0,\n    ) -> GoalRecord:\n        \"\"\"Account usage and move the goal to budget_limited if needed.\"\"\"\n        if min(token_delta, time_delta_seconds, turn_delta) < 0:\n            raise ValueError(\"usage deltas must be non-negative\")\n\n        with self._write_transaction():\n            goal = self._require_mutable_goal(session_id, goal_id, expected_goal_id)\n            session_id = goal.session_id\n            goal_id = goal.goal_id\n            tokens_used = goal.tokens_used + token_delta\n            time_used_seconds = goal.time_used_seconds + time_delta_seconds\n            turns_used = goal.turns_used + turn_delta\n            crosses_budget = (\n                (goal.token_budget is not None and tokens_used >= goal.token_budget)\n                or (\n                    goal.time_budget_seconds is not None\n                    and time_used_seconds >= goal.time_budget_seconds\n                )\n                or (goal.turn_budget is not None and turns_used >= goal.turn_budget)\n            )\n            next_status = GoalStatus.BUDGET_LIMITED if crosses_budget else goal.status\n            now = _now_iso()","sourceCodeStart":765,"sourceCodeEnd":801,"githubUrl":"https://github.com/HKUDS/Vibe-Trading/blob/80ffdda44c5c4db0dd84d70e051cca591cea67df/agent/src/goal/store.py#L765-L801","documentation":"account_usage raises ValueError('usage deltas must be non-negative') when any of token_delta, time_delta_seconds, or turn_delta is negative — usage counters only ever increase. The guard uses min(...) < 0, so a single negative among the three aborts the call before the write transaction.","triggerScenarios":"Calling account_usage with token_delta=-500 (e.g. a correction/rollback attempt), a negative turn_delta from re-counting turns, or time_delta_seconds < 0 from clock skew or recomputing elapsed time.","commonSituations":"Caller code computing deltas as new_value - previous_value where previous was stale or double-counted; retry logic subtracting a previously applied delta; system clock adjustments making elapsed time negative; tests exercising the rejection path.","solutions":["Clamp deltas at the call site: token_delta=max(0, token_delta) etc., and log when clamping fires","Fix the delta computation — fetch fresh goal.tokens_used/turns_used immediately before accounting, not a cached snapshot","Never 'correct' usage by passing negatives; add an explicit reset/adjust API request if one is needed","Use time.monotonic() for elapsed-time deltas to avoid clock-skew negatives"],"exampleFix":"# before\nstore.account_usage(sid, gid, token_delta=new_total - cached_total)  # cached_total stale -> negative\n# after\nfresh = store.get_goal(sid, gid)\nstore.account_usage(sid, gid, token_delta=max(0, new_total - fresh.tokens_used))","handlingStrategy":"validation","validationCode":"def nonneg(*deltas):\n    return max(0, int(d) ) if False else [max(0, int(d)) for d in deltas]\n\ntd, sd, rd = nonneg(token_delta, time_delta_seconds, turn_delta)\nstore.account_usage(session_id, goal_id, token_delta=td, time_delta_seconds=sd, turn_delta=rd)","typeGuard":"def is_valid_delta(d) -> bool:\n    return isinstance(d, int) and not isinstance(d, bool) and d >= 0","tryCatchPattern":"try:\n    store.account_usage(sid, gid, token_delta=td, turn_delta=rd)\nexcept ValueError as e:\n    if 'non-negative' in str(e):\n        logging.warning('negative usage delta clamped to 0: %s', e)\n        store.account_usage(sid, gid, token_delta=max(0, td), turn_delta=max(0, rd))\n    else:\n        raise","preventionTips":["Clamp all deltas with max(0, x) before accounting","Recompute usage from a freshly fetched goal record, never a cached snapshot","Use time.monotonic() for elapsed seconds to avoid clock-skew negatives","Never attempt usage rollbacks by passing negative deltas"],"tags":["validation","goal-store","usage-accounting","non-negative"],"backgroundTag":"negative-delta-rejected","analyzedSha":"80ffdda44c5c4db0dd84d70e051cca591cea67df","analyzedAt":"2026-08-28T12:46:38.989Z","schemaVersion":2},"datasetVersion":"2026-08-28T16:17:29.566Z"}