HKUDS/Vibe-Trading · error · ValueError

{name} must be positive

Error message

{name} must be positive

What it means

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.

Source

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

        """
        session_id = normalize_required_text(session_id, "session_id")
        objective = normalize_required_text(objective, "goal objective")
        reject_live_execution_objective(objective)
        if risk_tier is RiskTier.LIVE_TRADING_OR_EXECUTION:
            raise ValueError("live trading or execution goals are not supported")
        cleaned_criteria = [item.strip() for item in criteria if item.strip()]
        if not cleaned_criteria:
            raise ValueError("at least one goal criterion is required")
        for criterion in cleaned_criteria:
            reject_live_execution_objective(criterion)
        budgets = {
            "token_budget": token_budget,
            "turn_budget": turn_budget,
            "time_budget_seconds": time_budget_seconds,
        }
        for name, value in budgets.items():
            if value is not None and value <= 0:
                raise ValueError(f"{name} must be positive")

        now = _now_iso()
        goal_id = _id("goal")
        summary = ui_summary.strip() or objective[:80]
        current_values = [status.value for status in _CURRENT_STATUSES]
        placeholders = ",".join("?" for _ in current_values)

        with self._write_transaction():
            self._conn.execute(
                f"""
                UPDATE goals
                SET status = ?, updated_at = ?, completed_at = COALESCE(completed_at, ?)
                WHERE session_id = ? AND status IN ({placeholders})
                """,
                [GoalStatus.SUPERSEDED.value, now, now, session_id, *current_values],
            )
            self._conn.execute(
                """

View on GitHub (pinned to 80ffdda44c)

Solutions

  1. Pass None instead of 0 to mean 'no budget limit'
  2. Compute budgets with max(1, ...) when a small-but-set limit is intended
  3. Validate config at load: coerce 0 to None or fail with a clear message naming the field
  4. Check which of the three budgets is zero in the error message and fix that caller path

Example fix

# before
store.replace_goal(session_id="s1", objective=obj, criteria=c, token_budget=0, turn_budget=5)
# after
store.replace_goal(session_id="s1", objective=obj, criteria=c, token_budget=None, turn_budget=5)
Defensive patterns

Strategy: validation

Validate before calling

def clean_budget(v):
    return None if v is None or v <= 0 else int(v)

store.replace_goal(
    session_id=sid, objective=obj, criteria=cs,
    token_budget=clean_budget(token_budget),
    turn_budget=clean_budget(turn_budget),
    time_budget_seconds=clean_budget(time_budget_seconds),
)

Type guard

def is_valid_budget(v) -> bool:
    return v is None or (isinstance(v, int) and not isinstance(v, bool) and v > 0)

Try / catch

try:
    store.replace_goal(session_id=sid, objective=obj, criteria=cs, token_budget=tb)
except ValueError as e:
    if 'must be positive' in str(e):
        tb = None  # interpret 0 as unlimited
        store.replace_goal(session_id=sid, objective=obj, criteria=cs, token_budget=tb)
    else:
        raise

Prevention

When it happens

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

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

Related errors


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