{"record":{"id":"b0c1fb7caadd3b83","repo":"shareAI-lab/learn-claude-code","slug":"invalid-job-id","errorCode":null,"errorMessage":"invalid job ID","messagePattern":"invalid job ID","errorType":"console","errorClass":"ValueError","httpStatus":null,"severity":"warning","filePath":"s12_cron_scheduler/code.py","lineNumber":392,"sourceCode":"        return\n    try:\n        payload = json.loads(DURABLE_PATH.read_text())\n        if not isinstance(payload, list):\n            raise ValueError(\"expected a JSON list\")\n    except (OSError, json.JSONDecodeError, ValueError) as error:\n        print(f\"  [cron] could not load {DURABLE_PATH.name}: {error}\")\n        return\n\n    loaded = 0\n    with cron_lock:\n        for item in payload:\n            try:\n                job = CronJob(**item)\n                error = validate_cron(job.cron)\n                if error:\n                    raise ValueError(error)\n                if not job.id.startswith(\"cron_\"):\n                    raise ValueError(\"invalid job ID\")\n                if not job.prompt.strip():\n                    raise ValueError(\"prompt cannot be empty\")\n            except (TypeError, ValueError) as error:\n                print(f\"  [cron] skipped invalid saved job: {error}\")\n                continue\n            scheduled_jobs[job.id] = job\n            if job.pending_delivery:\n                cron_queue.append(job)\n            loaded += 1\n    if loaded:\n        print(f\"  [cron] loaded {loaded} durable job(s)\")\n\n\ndef new_cron_id() -> str:\n    for _ in range(100):\n        job_id = f\"cron_{secrets.token_hex(4)}\"\n        if job_id not in scheduled_jobs:\n            return job_id","sourceCodeStart":374,"sourceCodeEnd":410,"githubUrl":"https://github.com/shareAI-lab/learn-claude-code/blob/985456f4adea6f4df8fbad4112245dbd97444eae/s12_cron_scheduler/code.py#L374-L410","documentation":"Raised during load_durable_jobs() in s12_cron_scheduler/code.py:392 when a saved job's id does not start with the literal prefix 'cron_'. Job IDs are minted by new_cron_id() as 'cron_' + 8 hex chars, and the prefix is a cheap validity invariant enforced on reload; this per-job ValueError is caught and logged ('skipped invalid saved job'), and loading continues with the remaining jobs — one bad ID does not abort the whole scheduler load.","triggerScenarios":"A .scheduled_tasks.json entry whose id is 'job_1', an empty string, or a bare UUID; hand-edited files where the id field was renamed or dropped (a missing id raises TypeError, also skipped); IDs generated by an external script that uses its own naming scheme.","commonSituations":"Manual editing of the durable file; migrating from another scheduler whose IDs lack the cron_ prefix; version drift where an older build used different IDs.","solutions":["Restore the 'cron_' prefix on the id (and keep the rest of the 8-hex-char convention, e.g. 'cron_ab12cd34'), or regenerate the job via schedule_job().","When scripting job creation, always obtain IDs from new_cron_id() instead of inventing them.","After fixing, reload and confirm the '[cron] loaded N durable job(s)' count matches expectations."],"exampleFix":"# before: .scheduled_tasks.json entry\n{\"id\": \"nightly-build\", \"cron\": \"0 3 * * *\", \"prompt\": \"run build\"}\n\n# after\n{\"id\": \"cron_1a2b3c4d\", \"cron\": \"0 3 * * *\", \"prompt\": \"run build\", \"recurring\": true, \"durable\": true}","handlingStrategy":"validation","validationCode":"def job_id_is_valid(job_id) -> bool:\n    return isinstance(job_id, str) and job_id.startswith('cron_')","typeGuard":"def is_cron_job_id(value) -> bool:\n    return isinstance(value, str) and value.startswith('cron_')","tryCatchPattern":"# loader already skips bad jobs; catch at the scheduling boundary\nfor job in payload:\n    if not is_cron_job_id(job.get('id')):\n        log.warning('dropping job with foreign id %r', job.get('id'))\n        continue\n    register(job)","preventionTips":["Always mint IDs with new_cron_id(); never invent your own scheme.","After editing the durable file, count the 'loaded N durable job(s)' line against your expectations.","Lint the file for id prefixes before restarting the scheduler."],"tags":["cron","ids","persistence","validation"],"backgroundTag":null,"analyzedSha":"985456f4adea6f4df8fbad4112245dbd97444eae","analyzedAt":"2026-08-14T22:02:26.028Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}