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
- 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.
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
- 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.
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
- External event must be a JSON object
- External event kind must be one of {sorted(_VALID_KINDS)}; g
- Plugin manifest {manifest_path} must be a JSON object
- Marketplace URL must return a JSON object: {_redact_url_cred
- Marketplace manifest {manifest_path} must be a JSON object
AI-assisted analysis of langchain-ai/deepagents@a1af029e6e (2026-08-29).
Data as JSON: /api/errors/12035a6ed69aa35a.
Report an issue: GitHub.