HKUDS/Vibe-Trading · error · TypeError
'id', 'prompt', and 'schedule' must be strings
Error message
'id', 'prompt', and 'schedule' must be strings
What it means
Raised by ScheduledRun/Job from_dict (agent/src/scheduled_research/models.py:437) when any of the required top-level fields 'id', 'prompt', or 'schedule' is not a string. These are the core identifying fields of a job record; note they are fetched with data[...] first, so a missing key raises KeyError instead — this error is specifically about the wrong type.
Source
Thrown at agent/src/scheduled_research/models.py:437
def from_dict(cls, data: Dict[str, Any]) -> "ScheduledResearchJob":
"""Reconstruct a job from a plain dict.
Args:
data: A raw dict as produced by :meth:`to_dict`.
Returns:
The reconstructed ``ScheduledResearchJob``.
Raises:
KeyError: If a required field is missing.
TypeError: If a field has the wrong type.
ValueError: If ``status`` is not a recognized ``JobStatus`` value.
"""
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")View on GitHub (pinned to 80ffdda44c)
Solutions
- Cast ids: str(job_id) before building the dict
- Serialize schedule as its string expression (e.g. '0 9 * * 1-5' or '@daily')
- Ensure all three keys are present (else KeyError) and string-typed
Example fix
// before
record = {"id": job.pk, "prompt": prompt, "schedule": cron_obj}
// after
record = {"id": str(job.pk), "prompt": prompt, "schedule": "0 9 * * 1-5"} Defensive patterns
Strategy: validation
Validate before calling
def core_strings_ok(d):
return all(isinstance(d.get(k), str) and k in d for k in ("id", "prompt", "schedule")) Type guard
from typing import Any, Dict, Optional
def normalized_core(data: Dict[str, Any]) -> Optional[Dict[str, Any]]:
out = dict(data)
for k in ("id", "prompt", "schedule"):
if k in out and out[k] is not None:
out[k] = str(out[k])
missing = [k for k in ("id", "prompt", "schedule") if k not in out]
return None if missing else out Try / catch
try:
job = Job.from_dict(record)
except (TypeError, KeyError) as exc:
log.warning("dropping malformed job record: %r (%s)", record, exc)
job = None Prevention
- Always build records via constructors/to_dict, not literal dicts
- Stringify DB ids at the boundary
- Pass cron expressions as strings, never parsed objects
When it happens
Trigger: from_dict({"id": 42, "prompt": "...", "schedule": "@daily"}) or schedule passed as a cron object/crontab instance rather than its string expression. Missing keys raise KeyError, not this TypeError.
Common situations: Using a numeric DB primary key as job id; passing a parsed cron object or croniter instance instead of the raw expression string; building dicts programmatically and leaking non-str values.
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
- 'consecutive_failures' must be a non-negative integer
- 'last_error' must be a string or null
AI-assisted analysis of HKUDS/Vibe-Trading@80ffdda44c (2026-08-28).
Data as JSON: /api/errors/0717ab6241270026.
Report an issue: GitHub.