HKUDS/Vibe-Trading · error · TypeError
'title' must be a string
Error message
'title' must be a string
What it means
Raised by Job from_dict (agent/src/scheduled_research/models.py:478) when the optional 'title' field (default '') is present but is not a string. Titles are display metadata; any non-str value such as a number or list fails. The default when absent is the empty string, which passes.
Source
Thrown at agent/src/scheduled_research/models.py:478
# 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")
end_at = data.get("end_at")
if not isinstance(title, str):
raise TypeError("'title' must be a string")
if source_type not in {"prompt", "playbook"}:
raise ValueError("'source_type' must be 'prompt' or 'playbook'")
if playbook_slug is not None and not isinstance(playbook_slug, str):
raise TypeError("'playbook_slug' must be a string or null")
if end_at is not None and (isinstance(end_at, bool) or not isinstance(end_at, int)):
raise TypeError("'end_at' must be an integer (epoch ms) or null")
raw_config = data.get("config")
config: Dict[str, Any] = raw_config if isinstance(raw_config, dict) else {}
delivery_channel = data.get("delivery_channel")
delivery_target = data.get("delivery_target")
delivery_target_ref = data.get("delivery_target_ref")
delivery_target_label = data.get("delivery_target_label")
for name, value in (
("delivery_channel", delivery_channel),
("delivery_target", delivery_target),
("delivery_target_ref", delivery_target_ref),
("delivery_target_label", delivery_target_label),
):View on GitHub (pinned to 80ffdda44c)
Solutions
- Coerce to str: str(title) when building the record
- Validate user-supplied titles as strings at your API boundary
- Omit the field if no title
Example fix
// before
record["title"] = run_count
// after
record["title"] = f"Run #{run_count}" Defensive patterns
Strategy: validation
Validate before calling
def title_ok(d):
v = d.get("title", "")
return isinstance(v, str) Type guard
def safe_title(v, default=""):
return v if isinstance(v, str) else default Try / catch
try:
job = Job.from_dict(record)
except TypeError as exc:
if "title" in str(exc):
record = dict(record); record["title"] = str(record.get("title", ""))
job = Job.from_dict(record)
else:
raise Prevention
- Validate title as str at the API boundary
- Default to '' when generating records programmatically
- F-string any interpolated numbers into titles
When it happens
Trigger: {"title": 123} or {"title": ["Morning", "Digest"]}; absent key is fine because of the '' default.
Common situations: Titles derived from user numeric input or template lists; API layers that pass through untyped JSON bodies.
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
- 'consecutive_failures' must be a non-negative integer
AI-assisted analysis of HKUDS/Vibe-Trading@80ffdda44c (2026-08-28).
Data as JSON: /api/errors/a3d164fbd141999f.
Report an issue: GitHub.