You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Error: Exception raised 'id when missing from compare configuration #5472
Automated first-pass triage — a maintainer will review before any action is taken.
Classification: bug Priority: medium — the trigger is a config line, but a crash repeats every 5 minutes all day until it is fixed.
The bug:Compare.run_all() indexes every compare_list entry directly with tariff["id"] (compare.py:675), so an entry in apps.yaml that is missing its id line raises KeyError: 'id' — and because that entry produces no clear message about what is wrong, all the user sees is this error. Everything else in the crash follows from two code facts confirmed below.
What I found (investigated against current main, v9.3.7-4-gd7d8c607; the reporter runs v9.3.6 — confirmed from the attached log):
The log shows the first Scenario complete for tariff: Octopus Cosy line at 2026-10-10 00:00:20, then the KeyError: 'id' traceback at 00:00:21 — i.e. the first entry of the list (id current, name "Octopus Cosy") is processed fine and completed its scenario, and the crash happens when the loop reaches the next entry, on the line before Predbat logs anything about it. So the offending entry is the one after "Octopus Cosy" in the reporter's compare_list.
The comparison only runs on the daily midnight window (predbat.py:1432 — scheduled and minutes_now < RUN_EVERY) or while the "Predbat Compare Active" switch is on, which explains why there are no errors on 9 Oct (Predbat was restarted at 14:34 that day, after the midnight window) — the same config would have been crashing unseen at previous midnights only if the list already existed; either way the trigger is in the reporter's apps.yaml.
The error then repeats every 5 minutes all day (00:00 through the log's end at 13:15, 162 crashes, one per cycle). That is the second half of the bug: run_all() is called at predbat.py:1435 and the switch is cleared by expose_config("compare_active", False) at predbat.py:1436 — code that only runs if run_all() returns normally. A crash inside run_all() skips the clear, so the True the code itself just wrote stays on, and every following cycle re-enters the comparison and crashes again. Once the config is fixed, that loop self-clears within one cycle (compare completes, the switch resets to False).
Immediate fix: in apps.yaml, give every compare_list entry an id: line (unique, alphanumeric/underscore — the docs ask for this). The entry that follows "Octopus Cosy" is the one missing it. Removing that entry works too. The apps editor's Add-item template starts from name: + id:, so re-saving the list through the editor will also point at any entry that still lacks one.
What the code side should do (for a maintainer):run_single() already reads the id defensively (tariff.get("id", ""), compare.py:383) — run_all() should skip an id-less entry with a warning to the log rather than crashing the 5-minute cycle; and the compare_active reset at predbat.py:1436 should sit in a finally (the same pattern the code already uses for the active flag) so a comparison crash cannot re-arm itself every cycle. The apps editor already enforces id + name at save time (\_validate_compare_list(), web.py:4081) — that check is simply not reached when apps.yaml is edited by hand.
Related:#5133 recorded the same class of defect (a compare id that produces an invalid entity id, spamming 404s instead of crashing) and asked for warnings on invalid compare items — open PR #5143 fixes the publishing half of that but not this crash. PR #5465 touches the same midnight comparison window for a different defect (state left on the live plan). Not duplicates: the cause here is specifically an entry with no id.
Activity
springfall2008 commentedon Oct 10, 2026
Automated first-pass triage — a maintainer will review before any action is taken.
Classification: bug
Priority: medium — the trigger is a config line, but a crash repeats every 5 minutes all day until it is fixed.
The bug:
Compare.run_all()indexes everycompare_listentry directly withtariff["id"](compare.py:675), so an entry inapps.yamlthat is missing itsidline raisesKeyError: 'id'— and because that entry produces no clear message about what is wrong, all the user sees is this error. Everything else in the crash follows from two code facts confirmed below.What I found (investigated against current main, v9.3.7-4-gd7d8c607; the reporter runs v9.3.6 — confirmed from the attached log):
Scenario complete for tariff: Octopus Cosyline at 2026-10-10 00:00:20, then theKeyError: 'id'traceback at 00:00:21 — i.e. the first entry of the list (idcurrent, name "Octopus Cosy") is processed fine and completed its scenario, and the crash happens when the loop reaches the next entry, on the line before Predbat logs anything about it. So the offending entry is the one after "Octopus Cosy" in the reporter'scompare_list.predbat.py:1432—scheduled and minutes_now < RUN_EVERY) or while the "Predbat Compare Active" switch is on, which explains why there are no errors on 9 Oct (Predbat was restarted at 14:34 that day, after the midnight window) — the same config would have been crashing unseen at previous midnights only if the list already existed; either way the trigger is in the reporter'sapps.yaml.run_all()is called atpredbat.py:1435and the switch is cleared byexpose_config("compare_active", False)atpredbat.py:1436— code that only runs ifrun_all()returns normally. A crash insiderun_all()skips the clear, so the True the code itself just wrote stays on, and every following cycle re-enters the comparison and crashes again. Once the config is fixed, that loop self-clears within one cycle (compare completes, the switch resets to False).Immediate fix: in
apps.yaml, give everycompare_listentry anid:line (unique, alphanumeric/underscore — the docs ask for this). The entry that follows "Octopus Cosy" is the one missing it. Removing that entry works too. The apps editor's Add-item template starts fromname:+id:, so re-saving the list through the editor will also point at any entry that still lacks one.What the code side should do (for a maintainer):
run_single()already reads the id defensively (tariff.get("id", ""), compare.py:383) —run_all()should skip an id-less entry with a warning to the log rather than crashing the 5-minute cycle; and thecompare_activereset atpredbat.py:1436should sit in afinally(the same pattern the code already uses for theactiveflag) so a comparison crash cannot re-arm itself every cycle. The apps editor already enforcesid+nameat save time (\_validate_compare_list(), web.py:4081) — that check is simply not reached whenapps.yamlis edited by hand.Related: #5133 recorded the same class of defect (a compare id that produces an invalid entity id, spamming 404s instead of crashing) and asked for warnings on invalid compare items — open PR #5143 fixes the publishing half of that but not this crash. PR #5465 touches the same midnight comparison window for a different defect (state left on the live plan). Not duplicates: the cause here is specifically an entry with no
id.[-]Error: Exception raised 'id[/-][+]Error: Exception raised 'id when missing from compare configuration[/+]test(compare): behavioural tests for the id-less skip and compare_act…
fix(compare): validate compare_list once, skipping every malformed en…
fix(web): share compare.py's compare_list rules in the editor and com…
fix(compare): one check_compare_list() for engine, editor and page (#…
fix(compare): reject a non-mapping config, blank name; report every p…
fix(compare): keep treating an empty config: list or string as no ove…
fix(compare): spell out which config: values mean no overrides (#5472)
test(compare): pin which config: values are kept, dropped or skipped (#…
fix(compare): skip malformed compare_list entries instead of crashing…