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

  1. Cast ids: str(job_id) before building the dict
  2. Serialize schedule as its string expression (e.g. '0 9 * * 1-5' or '@daily')
  3. 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

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


AI-assisted analysis of HKUDS/Vibe-Trading@80ffdda44c (2026-08-28). Data as JSON: /api/errors/0717ab6241270026. Report an issue: GitHub.