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
- 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
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
- 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
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
- evidence limit must be positive
- {field_name} cannot be empty
- at least one goal criterion is required
- evidence text cannot be empty
- usage deltas must be non-negative
AI-assisted analysis of HKUDS/Vibe-Trading@80ffdda44c (2026-08-28).
Data as JSON: /api/errors/cbc28065ec305fbf.
Report an issue: GitHub.