HKUDS/Vibe-Trading · error · TypeError
'consecutive_failures' must be a non-negative integer
Error message
'consecutive_failures' must be a non-negative integer
What it means
Raised by Job from_dict (agent/src/scheduled_research/models.py:451) when 'consecutive_failures' is a bool, non-int, or negative. It counts successive failures for backoff/disable logic, so it must be a non-negative integer; bool is explicitly rejected because it subclasses int. Defaults to 0 when absent.
Source
Thrown at agent/src/scheduled_research/models.py:451
job_id = data["id"]
prompt = data["prompt"]
schedule = data["schedule"]
if not isinstance(job_id, str) or not isinstance(prompt, str) or not isinstance(schedule, str):
raise TypeError("'id', 'prompt', and 'schedule' must be strings")
next_run_at = data["next_run_at"]
created_at = data["created_at"]
if not isinstance(next_run_at, int) or not isinstance(created_at, int):
raise TypeError("'next_run_at' and 'created_at' must be integers (epoch ms)")
last_run_at = data.get("last_run_at")
if last_run_at is not None and not isinstance(last_run_at, int):
raise TypeError("'last_run_at' must be an integer (epoch ms) or null")
consecutive_failures = data.get("consecutive_failures", 0)
if (
isinstance(consecutive_failures, bool)
or not isinstance(consecutive_failures, int)
or consecutive_failures < 0
):
raise TypeError("'consecutive_failures' must be a non-negative integer")
last_error = data.get("last_error")
failure_kind = data.get("failure_kind")
if last_error is not None and not isinstance(last_error, str):
raise TypeError("'last_error' must be a string or null")
if failure_kind is not None and failure_kind not in {"dispatch", "schedule"}:
raise ValueError("'failure_kind' must be 'dispatch', 'schedule', or null")
# Never raises: the store quarantines the whole file when a single
# record fails to load, so an unusable timezone value degrades that
# one job to UTC — the semantics it had before the field existed —
# instead of taking every other job down with it. Absent, blank, and
# non-string values all normalize to None.
raw_tz = data.get("timezone")
tz = raw_tz if isinstance(raw_tz, str) and raw_tz.strip() else None
if raw_tz is not None and tz is None:
logger.warning(
"scheduled research job %s has an unusable timezone %r; "
"evaluating its schedule in UTC",
job_id,View on GitHub (pinned to 80ffdda44c)
Solutions
- Clamp counters: max(0, failures)
- Keep counter arithmetic in int and avoid bools
- Omit the field to default to 0
Example fix
// before record["consecutive_failures"] = failures - 1 // after record["consecutive_failures"] = max(0, failures - 1)
Defensive patterns
Strategy: validation
Validate before calling
def failures_ok(v):
return not isinstance(v, bool) and isinstance(v, int) and v >= 0 Type guard
def safe_failure_count(v, default=0):
if isinstance(v, bool) or not isinstance(v, int) or v < 0:
return default
return v Try / catch
try:
job = Job.from_dict(record)
except TypeError as exc:
if "consecutive_failures" in str(exc):
record = dict(record); record["consecutive_failures"] = 0
job = Job.from_dict(record)
else:
raise Prevention
- Clamp failure counters with max(0, n)
- Never let bools into numeric fields
- Centralize failure bookkeeping in one helper
When it happens
Trigger: {"consecutive_failures": -1} after resetting logic underflows, {"consecutive_failures": "2"} from stringified JSON, or {"consecutive_failures": True} from a flag mistakenly stored here.
Common situations: Retry bookkeeping that decrements below zero; JSON configs hand-edited with quoted numbers; storing a boolean 'failed' flag in the counter slot.
Related errors
- 'delivery' must be an object or null
- 'delivery.{name}' must be a string or null
- 'delivery.attempts' must be a non-negative integer
- 'id', 'prompt', and 'schedule' must be strings
- 'last_error' must be a string or null
AI-assisted analysis of HKUDS/Vibe-Trading@80ffdda44c (2026-08-28).
Data as JSON: /api/errors/03b9f6b9443a7121.
Report an issue: GitHub.