HKUDS/Vibe-Trading · error · ValueError

usage deltas must be non-negative

Error message

usage deltas must be non-negative

What it means

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.

Source

Thrown at agent/src/goal/store.py:783

        updated = self.get_goal(goal_id)
        if updated is None:
            raise RuntimeError("updated goal could not be reloaded")
        return updated

    @_synchronized
    def account_usage(
        self,
        *,
        session_id: str,
        goal_id: str,
        expected_goal_id: str,
        token_delta: int = 0,
        time_delta_seconds: int = 0,
        turn_delta: int = 0,
    ) -> GoalRecord:
        """Account usage and move the goal to budget_limited if needed."""
        if min(token_delta, time_delta_seconds, turn_delta) < 0:
            raise ValueError("usage deltas must be non-negative")

        with self._write_transaction():
            goal = self._require_mutable_goal(session_id, goal_id, expected_goal_id)
            session_id = goal.session_id
            goal_id = goal.goal_id
            tokens_used = goal.tokens_used + token_delta
            time_used_seconds = goal.time_used_seconds + time_delta_seconds
            turns_used = goal.turns_used + turn_delta
            crosses_budget = (
                (goal.token_budget is not None and tokens_used >= goal.token_budget)
                or (
                    goal.time_budget_seconds is not None
                    and time_used_seconds >= goal.time_budget_seconds
                )
                or (goal.turn_budget is not None and turns_used >= goal.turn_budget)
            )
            next_status = GoalStatus.BUDGET_LIMITED if crosses_budget else goal.status
            now = _now_iso()

View on GitHub (pinned to 80ffdda44c)

Solutions

  1. Clamp deltas at the call site: token_delta=max(0, token_delta) etc., and log when clamping fires
  2. Fix the delta computation — fetch fresh goal.tokens_used/turns_used immediately before accounting, not a cached snapshot
  3. Never 'correct' usage by passing negatives; add an explicit reset/adjust API request if one is needed
  4. Use time.monotonic() for elapsed-time deltas to avoid clock-skew negatives

Example fix

# before
store.account_usage(sid, gid, token_delta=new_total - cached_total)  # cached_total stale -> negative
# after
fresh = store.get_goal(sid, gid)
store.account_usage(sid, gid, token_delta=max(0, new_total - fresh.tokens_used))
Defensive patterns

Strategy: validation

Validate before calling

def nonneg(*deltas):
    return max(0, int(d) ) if False else [max(0, int(d)) for d in deltas]

td, sd, rd = nonneg(token_delta, time_delta_seconds, turn_delta)
store.account_usage(session_id, goal_id, token_delta=td, time_delta_seconds=sd, turn_delta=rd)

Type guard

def is_valid_delta(d) -> bool:
    return isinstance(d, int) and not isinstance(d, bool) and d >= 0

Try / catch

try:
    store.account_usage(sid, gid, token_delta=td, turn_delta=rd)
except ValueError as e:
    if 'non-negative' in str(e):
        logging.warning('negative usage delta clamped to 0: %s', e)
        store.account_usage(sid, gid, token_delta=max(0, td), turn_delta=max(0, rd))
    else:
        raise

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of HKUDS/Vibe-Trading@80ffdda44c (2026-08-28). Data as JSON: /api/errors/47e96bbdc074f04e. Report an issue: GitHub.