langchain-ai/deepagents · error · CronJobError

cron jobs file must contain a JSON list

Error message

cron jobs file must contain a JSON list

What it means

CronJobStore._read_jobs reads the cron jobs JSON file and requires its top-level structure to be a JSON list of job objects. If the file exists but contains a JSON object, string, number, or other non-list value, CronJobError is raised because the store cannot deserialize jobs from it.

Source

Thrown at libs/talon/deepagents_talon/cron/jobs.py:623

        removed: list[CronJob] = []
        for job in self.list_jobs():
            reference = job.last_run_at or job.created_at
            if not job.enabled and job.next_run_at is None and reference <= cutoff:
                removed.append(job)
            else:
                kept.append(job)
        if removed:
            self._write_jobs(kept)
        return removed

    def _read_jobs(self) -> list[CronJob]:
        self._ensure_store()
        if not self.path.exists():
            return []
        data = json.loads(self.path.read_text(encoding="utf-8"))
        if not isinstance(data, list):
            msg = "cron jobs file must contain a JSON list"
            raise CronJobError(msg)
        return [CronJob.from_dict(cast("CronJobDict", item)) for item in data]

    def _write_jobs(self, jobs: list[CronJob]) -> None:
        self._ensure_store()
        payload = json.dumps(
            [job.to_dict() for job in jobs],
            indent=2,
            sort_keys=True,
        )
        fd, name = tempfile.mkstemp(
            dir=self.cron_dir,
            prefix=".jobs.",
            suffix=".tmp",
            text=True,
        )
        tmp_path = Path(name)
        try:
            with os.fdopen(fd, "w", encoding="utf-8") as file:

View on GitHub (pinned to a1af029e6e)

Solutions

  1. Open the jobs file (the store's path inside its cron dir) and restructure the top level to a bare JSON list of job objects.
  2. If the content is wrapped (e.g. {"jobs": [...]}), unwrap it: replace the file with just the list value.
  3. If the data is unrecoverable or stale, back it up and delete the file so the store recreates an empty list.
  4. Never hand-edit the store while sessions run; use the store API (list/create/remove) to mutate state.

Example fix

// before (jobs file)
{"jobs": [{"id": "a", "...": "..."}]}
// after
[{"id": "a", "...": "..."}]
Defensive patterns

Strategy: type-guard

Validate before calling

import json
from pathlib import Path

data = json.loads(Path(store.path).read_text())
if not isinstance(data, list):
    Path(store.path).write_text(json.dumps(list(data.get("jobs", []))))

Type guard

def is_job_list(data: object) -> bool:
    return isinstance(data, list) and all(isinstance(j, dict) and "id" in j for j in data)

Try / catch

try:
    jobs = store.list_jobs()
except CronJobError:
    backup(store.path)
    store.path.unlink()
    jobs = []

Prevention

When it happens

Trigger: The jobs.json file was hand-edited into a dict shape (e.g. {"jobs": [...]}) instead of a bare list; another tool or an older/newer version wrote a different schema; a script overwrote the file with wrapped or exported JSON.

Common situations: Manual debugging edits to the cron state file; migrating state between machines with a transformed export; a corrupted or partially rewritten file from a concurrent writer that ignored the store's atomic-write format.

Understand the failure class

Background: Schema validation failed / invalid input schema: payload rejected because its shape doesn't match the expected schema — this error's family across 28 libraries.

Related errors


AI-assisted analysis of langchain-ai/deepagents@a1af029e6e (2026-08-29). Data as JSON: /api/errors/12035a6ed69aa35a. Report an issue: GitHub.