fix: normalize repeat field in update_job() to prevent TypeError (#15582) - #15682
fix: normalize repeat field in update_job() to prevent TypeError (#15582)#15682vominh1919 wants to merge 2 commits into
Conversation
The /api/skills endpoint calls _find_all_skills() without a try/except. If skill discovery raises (e.g. corrupted skills directory, permission error), the endpoint returns a 500 Internal Server Error. Wrap the call in try/except and return an empty list on failure, matching the pattern used by _list_all_skills() in skills_config.py. Fixes NousResearch#15486
update_job() merges updates directly via {**job, **updates}, which
overwrites the repeat dict structure {"times": N, "completed": 0}
with a raw integer when the API passes repeat as an int.
This causes mark_job_run() to crash with TypeError when it tries
to subscript the integer: job["repeat"]["completed"].
Add the same normalization that create_job() applies, converting
raw integers to the expected dict structure.
Fixes NousResearch#15582
|
duplicate comment posted |
|
I checked #15590. The overlap is real: both PRs are fixing repeat normalization in Given that, I would prefer to consolidate onto one PR. Unless there's an important behavioral difference not captured in the current discussion, #15590 currently looks like the better place to land the fix rather than merging two variants. |
|
Noting the overlap for consolidation: #18416 is now open as a broader tested fix for #15582 after checking this PR and #15590. It keeps the same core normalization idea, but also covers:
If maintainers prefer one landing path, #18416 is intended to supersede this variant rather than merging two overlapping fixes. |
teknium1
left a comment
There was a problem hiding this comment.
Thanks for identifying the raw-form repeat mismatch. The premise remains valid on current main: cron/jobs.py:1220 blindly merges updates, while the dashboard accepts CronJobUpdate.updates: dict at hermes_cli/web_server.py:9935; mark_job_run() then calls .get() on repeat at cron/jobs.py:1412.
Problems
- The new
(int, float)branch accepts booleans and truncates floats. Because the API payload is untyped, validation must excludebooland reject non-integer values. - The proposed
{"times": val, "completed": 0}replacement resets completed runs when an existing job's limit is edited. Preserve the stored completed count. - Existing scalar records remain unsafe in
mark_job_run()(cron/jobs.py:1412),claim_dispatch()(cron/jobs.py:1497), and due-job handling (cron/jobs.py:1816). gh pr diff 15682shows no regression tests, and commitd1d956da32a7adds an unrelated/api/skillschange for #15486.
Suggested changes
- Add strict repeat validation and explicit null semantics at the update boundary, preserve completed counts, repair legacy records at every read/run boundary, and add storage/API regressions.
- Split the skills-endpoint change for separate review.
This is an automated hermes-sweeper review.
| elif isinstance(raw_repeat, dict): | ||
| raw_repeat.setdefault("times", None) | ||
| raw_repeat.setdefault("completed", 0) | ||
|
|
There was a problem hiding this comment.
CronJobUpdate.updates is an untyped dictionary on current main (hermes_cli/web_server.py:9935), so this also accepts True (bool is an int subclass) and truncates floats. Restrict the update contract to positive non-boolean integers, with explicit null semantics, and cover it at the API boundary.
| raw_repeat.setdefault("completed", 0) | ||
|
|
||
| schedule_changed = "schedule" in updates | ||
|
|
There was a problem hiding this comment.
This resets an already-running job's completed count whenever its repeat limit is edited, allowing previously consumed executions to recur. Preserve the existing job['repeat']['completed'] while replacing the limit.
Related: #18416 (now open) is a broader, tested fix for the same bug (#15582) — it keeps the same |
Problem
update_job()merges updates via{**job, **updates}, which blindly overwrites therepeatdict structure{"times": N, "completed": 0}with a raw integer when the API passesrepeatas an int.This causes
mark_job_run()to crash withTypeError: 'int' object is not subscriptablewhen it triesjob["repeat"]["completed"].Fix
Add repeat field normalization in
update_job(), mirroring whatcreate_job()already applies:repeatis int/float: convert to{"times": val, "completed": 0}dictrepeatis a dict: ensure"times"and"completed"keys exist viasetdefaultBefore vs After
PATCH /api/jobs/{id}withrepeat: 5mark_job_run()crashes{"times": 5, "completed": 0}Fixes #15582