HKUDS/Vibe-Trading · error · ValueError
'failure_kind' must be 'dispatch', 'schedule', or null
Error message
'failure_kind' must be 'dispatch', 'schedule', or null
What it means
Raised by Job from_dict (agent/src/scheduled_research/models.py:457) when 'failure_kind' is present, non-null, and not one of the literals 'dispatch' or 'schedule'. It classifies whether the last failure happened in dispatching the run or computing the schedule; any other string (or non-string) is rejected.
Source
Thrown at agent/src/scheduled_research/models.py:457
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,
raw_tz,
)
status = JobStatus(data["status"])
title = data.get("title", "")
source_type = data.get("source_type", "prompt")
playbook_slug = data.get("playbook_slug")View on GitHub (pinned to 80ffdda44c)
Solutions
- Use exact lowercase literals 'dispatch' or 'schedule'
- Map new categories to one of these two or null until the enum is extended
- Normalize empty strings to null before persisting
Example fix
// before
record["failure_kind"] = kind.title() # 'Dispatch'
// after
record["failure_kind"] = kind if kind in {"dispatch", "schedule"} else None Defensive patterns
Strategy: validation
Validate before calling
VALID_KINDS = {"dispatch", "schedule"}
def failure_kind_ok(v):
return v is None or (isinstance(v, str) and v in VALID_KINDS) Type guard
VALID_KINDS = {"dispatch", "schedule"}
def safe_failure_kind(v):
if isinstance(v, str):
v = v.lower()
if v in VALID_KINDS:
return v
return None Try / catch
try:
job = Job.from_dict(record)
except ValueError as exc:
if "failure_kind" in str(exc):
record = dict(record); record["failure_kind"] = None
job = Job.from_dict(record)
else:
raise Prevention
- Write failure_kind only from a shared constant set
- Lowercase before persisting
- Use null, not empty string, for 'no classification'
When it happens
Trigger: {"failure_kind": "Dispatch"} (wrong case), {"failure_kind": "timeout"}, or {"failure_kind": ""}. Only exactly 'dispatch', 'schedule', or null pass.
Common situations: Introducing a new failure category in one component before the models module knows it; case inconsistencies between writer and reader; empty string sent instead of null by form/JSON handling.
Understand the failure class
Background: Invalid enum value errors: "Unknown type", "Invalid scope", "must be one of" — when a string is not on the library's allowed list — this error's family across 23 libraries.
Related errors
- unknown delivery status {raw_status!r}
- 'source_type' must be 'prompt' or 'playbook'
- source.kind must be 'prompt' or 'playbook'
- delivery.mode must be 'in_app', 'origin', or 'configured'
- invalid risk_tier: {req.risk_tier}
AI-assisted analysis of HKUDS/Vibe-Trading@80ffdda44c (2026-08-28).
Data as JSON: /api/errors/aee657744d2f6f02.
Report an issue: GitHub.