{"record":{"id":"12035a6ed69aa35a","repo":"langchain-ai/deepagents","slug":"cron-jobs-file-must-contain-a-json-list","errorCode":null,"errorMessage":"cron jobs file must contain a JSON list","messagePattern":"cron jobs file must contain a JSON list","errorType":"validation","errorClass":"CronJobError","httpStatus":null,"severity":"error","filePath":"libs/talon/deepagents_talon/cron/jobs.py","lineNumber":623,"sourceCode":"        removed: list[CronJob] = []\n        for job in self.list_jobs():\n            reference = job.last_run_at or job.created_at\n            if not job.enabled and job.next_run_at is None and reference <= cutoff:\n                removed.append(job)\n            else:\n                kept.append(job)\n        if removed:\n            self._write_jobs(kept)\n        return removed\n\n    def _read_jobs(self) -> list[CronJob]:\n        self._ensure_store()\n        if not self.path.exists():\n            return []\n        data = json.loads(self.path.read_text(encoding=\"utf-8\"))\n        if not isinstance(data, list):\n            msg = \"cron jobs file must contain a JSON list\"\n            raise CronJobError(msg)\n        return [CronJob.from_dict(cast(\"CronJobDict\", item)) for item in data]\n\n    def _write_jobs(self, jobs: list[CronJob]) -> None:\n        self._ensure_store()\n        payload = json.dumps(\n            [job.to_dict() for job in jobs],\n            indent=2,\n            sort_keys=True,\n        )\n        fd, name = tempfile.mkstemp(\n            dir=self.cron_dir,\n            prefix=\".jobs.\",\n            suffix=\".tmp\",\n            text=True,\n        )\n        tmp_path = Path(name)\n        try:\n            with os.fdopen(fd, \"w\", encoding=\"utf-8\") as file:","sourceCodeStart":605,"sourceCodeEnd":641,"githubUrl":"https://github.com/langchain-ai/deepagents/blob/a1af029e6e73cb17c36bff823d227747b28e91e1/libs/talon/deepagents_talon/cron/jobs.py#L605-L641","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["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.","If the content is wrapped (e.g. {\"jobs\": [...]}), unwrap it: replace the file with just the list value.","If the data is unrecoverable or stale, back it up and delete the file so the store recreates an empty list.","Never hand-edit the store while sessions run; use the store API (list/create/remove) to mutate state."],"exampleFix":"// before (jobs file)\n{\"jobs\": [{\"id\": \"a\", \"...\": \"...\"}]}\n// after\n[{\"id\": \"a\", \"...\": \"...\"}]","handlingStrategy":"type-guard","validationCode":"import json\nfrom pathlib import Path\n\ndata = json.loads(Path(store.path).read_text())\nif not isinstance(data, list):\n    Path(store.path).write_text(json.dumps(list(data.get(\"jobs\", []))))","typeGuard":"def is_job_list(data: object) -> bool:\n    return isinstance(data, list) and all(isinstance(j, dict) and \"id\" in j for j in data)","tryCatchPattern":"try:\n    jobs = store.list_jobs()\nexcept CronJobError:\n    backup(store.path)\n    store.path.unlink()\n    jobs = []","preventionTips":["Never hand-edit the jobs file while sessions may be running; use the store API.","Back up the cron dir before manual migrations or exports.","Validate any externally produced JSON against the bare-list schema before placing it in the store."],"tags":["cron","json","corrupt-state","schema"],"backgroundTag":"schema-validation-failed","analyzedSha":"a1af029e6e73cb17c36bff823d227747b28e91e1","analyzedAt":"2026-08-29T11:43:24.718Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}