Fix asset-level scheduling crash for a single-entry flex-model list without a top-level sensor - #2361
Draft
Flix6x wants to merge 2 commits into
Draft
Fix asset-level scheduling crash for a single-entry flex-model list without a top-level sensor#2361Flix6x wants to merge 2 commits into
Flix6x wants to merge 2 commits into
Conversation
…duling
An asset-level scheduling trigger whose flex-model list contains a single
device entry without a top-level "sensor" key (e.g. a battery referencing
its power sensor only via a nested output reference, like
{"consumption": {"sensor": N}, "state-of-charge": {"sensor": M}})
crashed before scheduling with:
AttributeError: 'NoneType' object has no attribute 'generic_asset'
collect_flex_config unwrapped any single-entry flex-model list without a
top-level "sensor" key into a bare dict, which downstream is deserialized
in single-sensor mode - resolving the device against self.sensor, which is
None for asset-level scheduling. The same entry in a multi-entry list
scheduled fine, because multi-device (asset) mode resolves nested output
sensor references (see _resolve_power_sensor).
Only unwrap when scheduling a specific sensor (self.sensor is not None);
for asset-level scheduling, keep the list form (multi-device mode) even
for a single device entry. As a fail-safe, single-sensor (dict) flex-model
deserialization without a sensor now raises a clear ValueError naming the
flex-model entry, so the AttributeError cannot recur through any code path.
Discovered during PR #2360 (whose regression test is multi-device for
exactly this reason).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WtuVTVfL4fQ9QSqbLXmAGD
Signed-off-by: F.N. Claessen <claessen@seita.nl>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WtuVTVfL4fQ9QSqbLXmAGD Signed-off-by: F.N. Claessen <claessen@seita.nl>
Documentation build overview
|
Flix6x
added a commit
that referenced
this pull request
Jul 27, 2026
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WtuVTVfL4fQ9QSqbLXmAGD Signed-off-by: F.N. Claessen <claessen@seita.nl>
Flix6x
commented
Jul 27, 2026
|
|
||
| Bugfixes | ||
| ----------- | ||
| * Fix asset-level scheduling crashing with ``AttributeError: 'NoneType' object has no attribute 'generic_asset'`` when the flex-model list contains a single device entry without a top-level ``sensor`` key (e.g. a device referencing its power sensor only via a nested output reference, like ``{"consumption": {"sensor": ...}}``); such a flex-model now stays in multi-device mode, and a clear ``ValueError`` is raised if a device's power sensor genuinely cannot be resolved [see `PR #2361 <https://www.github.com/FlexMeasures/flexmeasures/pull/2361>`_] |
Member
Author
There was a problem hiding this comment.
I think this bug is unreleased. Append the PR reference to the changelog entry for the new consumption field instead.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
An asset-level scheduling trigger whose flex-model list contains a single device entry without a top-level
"sensor"key — e.g. one battery entry shaped[ { "consumption": {"sensor": N}, "state-of-charge": {"sensor": M}, "soc-at-start": "0.2 MWh", "power-capacity": "1 MW" } ]crashed before scheduling (at job creation, during
deserialize_config) with:The same device entry in a multi-entry list scheduled fine — which is why this went unnoticed, and why PR #2360's regression test is multi-device. Discovered during the work on #2360 and deliberately left out of its scope.
Root cause
collect_flex_configunwraps ("delistifies") any single-entry flex-model list whose entry lacks a top-level"sensor"key into a bare dict. A bare dict flex-model is deserialized downstream in single-sensor mode, which resolves the device againstself.sensor— but for asset-level scheduling there is no sensor (self.sensor is None), so it crashed. Only multi-device (asset) mode knows how to resolve a device's power sensor from nested references such as{"consumption": {"sensor": N}}(via_resolve_power_sensor, since #2321).The fix (smallest, least surprising change)
collect_flex_confignow only reverts the flex-model listification when scheduling a specific sensor (self.sensor is not None). That unwrap exists to revert the listification this method itself applies to bare-dict (sensor-level) flex-models; for asset-level scheduling the flex-model stays a list, keeping multi-device mode, where nested sensor references already resolve correctly. The alternative — teaching single-sensor mode to resolve nested references — would have duplicated multi-device logic in a mode that is defined by resolving against one given sensor, and would have changed a lot more code.Schedulerconstruction),MetaStorageScheduler._deserialize_flex_modelnow raises a clearValueErrornaming the flex-model entry, instead of theAttributeError. The crash is now impossible through any code path.Tests
test_scheduling_single_device_with_nested_output_sensor_only(mirrors Fix scheduling jobs failing on a timed_belief UniqueViolation for devices with nested-output-only power sensors #2360's regression test, but with a single-entry flex-model list): asserts the asset-level job schedules successfully and the nested consumption output sensor receives exactly one series of beliefs (96 quarter-hours). Verified to fail onmainwith the exactAttributeErrorabove, and to pass with this fix.test_collect_flex_config_keeps_single_entry_list_for_asset: unit test that asset-level scheduling keeps a single-entry list as a list, while sensor-level scheduling still unwraps to single-sensor (dict) mode.test_deserialize_dict_flex_model_without_sensor_raises: unit test for the fail-safeValueError.Interaction with #2360 (not a dependency)
This PR is based on
origin/mainand does not overlap with #2360's diff (no merge conflicts expected; #2360 touches the scheduler's output assembly, this PR touches flex-config collection/deserialization). One behavioral interaction to be aware of: with this fix, a single nested-output-only device reaches the output assembly, where — without #2360 — its power sensor is still emitted both as astorage_scheduleand as aconsumption_scheduleoutput. The regression test here stays green on plainmainbecause its flat prices make both outputs identical (sosave_to_dbskips the duplicate as an unchanged belief), but non-trivial schedules for such devices still need #2360's fix to save. Merging both PRs (in either order) fully resolves the field scenario in which the crash was found.🤖 Generated with Claude Code
https://claude.ai/code/session_01WtuVTVfL4fQ9QSqbLXmAGD