shareAI-lab/learn-claude-code · error · RuntimeError
Could not allocate a cron job ID
Error message
Could not allocate a cron job ID
What it means
Raised by new_cron_id() in s12_cron_scheduler/code.py:411 after 100 attempts to mint an unused 'cron_' + 8-hex-char ID, every one of which was already present in the in-memory scheduled_jobs dict. IDs come from secrets.token_hex(4) (32 bits); with a normal number of jobs, 100 random draws colliding is astronomically improbable, so hitting this error in practice signals a degenerate state — e.g. scheduled_jobs pre-populated with an enormous or exhaustive ID set, or a bug seeding the dict with every generated candidate.
Source
Thrown at s12_cron_scheduler/code.py:411
if not job.prompt.strip():
raise ValueError("prompt cannot be empty")
except (TypeError, ValueError) as error:
print(f" [cron] skipped invalid saved job: {error}")
continue
scheduled_jobs[job.id] = job
if job.pending_delivery:
cron_queue.append(job)
loaded += 1
if loaded:
print(f" [cron] loaded {loaded} durable job(s)")
def new_cron_id() -> str:
for _ in range(100):
job_id = f"cron_{secrets.token_hex(4)}"
if job_id not in scheduled_jobs:
return job_id
raise RuntimeError("Could not allocate a cron job ID")
def schedule_job(cron: str, prompt: str, recurring: bool = True,
durable: bool = True) -> CronJob | str:
error = validate_cron(cron)
if error:
return error
if not prompt.strip():
return "Prompt cannot be empty"
with cron_lock:
job = CronJob(
id=new_cron_id(),
cron=cron,
prompt=prompt,
recurring=recurring,
durable=durable,
)View on GitHub (pinned to 985456f4ad)
Solutions
- Inspect len(scheduled_jobs) and the contents of .scheduled_tasks.json — if the dict is unexpectedly full of IDs, the state is corrupted; clear or prune it.
- Widen the ID space (e.g. secrets.token_hex(8) with the matching prefix check) if you legitimately need billions of jobs, and migrate old IDs.
- In code that manages scheduled_jobs manually, never insert a candidate ID before new_cron_id() returns it.
Example fix
# before
job_id = f'cron_{secrets.token_hex(4)}'
# after (wider space; also relax the reload prefix check to accept 16 hex chars)
job_id = f'cron_{secrets.token_hex(8)}' Defensive patterns
Strategy: retry
Validate before calling
def id_space_is_healthy(scheduled_jobs: dict) -> bool:
return len(scheduled_jobs) < 1_000_000 # far below any realistic collision risk Try / catch
try:
job_id = new_cron_id()
except RuntimeError:
# state is degenerate; clear or prune scheduled_jobs, then retry once
prune_or_reset_scheduled_jobs()
job_id = new_cron_id() Prevention
- Never insert candidate IDs into scheduled_jobs yourself; let new_cron_id() manage allocation.
- Keep .scheduled_tasks.json free of machine-generated ID floods.
- If you truly need massive job counts, widen the ID to token_hex(8) and migrate.
When it happens
Trigger: A seeded/mocked scheduled_jobs containing huge numbers of IDs (approaching 2^32) before calling new_cron_id(); a loop bug that inserts each candidate ID before checking, guaranteeing collisions; corrupted state loaded from a tampered .scheduled_tasks.json with an ID flood. Real-world stores with dozens of jobs cannot trigger this.
Common situations: Tests that exhaustively seed the ID space to verify the guard; tampered or machine-generated durable files containing thousands of IDs (still far from the threshold); bugs in code that snapshots and restores scheduled_jobs.
Related errors
- Could not allocate a unique task ID
- invalid job ID
- Invalid task ID: {task_id!r}
- Task file ID does not match {task_id}
- expected a JSON list
AI-assisted analysis of shareAI-lab/learn-claude-code@985456f4ad (2026-08-14).
Data as JSON: /api/errors/d4c251df258eb15d.
Report an issue: GitHub.