Skip to content

fix(config): refuse camera settings without a storage device - #3448

Open
JamBalaya56562 wants to merge 1 commit into
motioneye-project:devfrom
JamBalaya56562:fix/storage-device-null
Open

JamBalaya56562 wants to merge 1 commit into
motioneye-project:devfrom
JamBalaya56562:fix/storage-device-null

Conversation

@JamBalaya56562

@JamBalaya56562 JamBalaya56562 commented Oct 12, 2026 •

Copy link
Copy Markdown
Contributor

What

Camera settings whose storage_device is null, empty or missing are refused with a ValueError (Setting "storage_device" must be set). A new input_required(ui, key) next to input_sanity_check() returns a set value and raises for a missing key, None or ''; False and 0 count as set, so it can be used for other required keys later. It is called where @storage_device is put into the camera config.

Why

  • "storage_device": null raised AttributeError: 'NoneType' object has no attribute 'startswith'.
  • A missing field raised KeyError: 'storage_device'.
  • An empty string was silently stored as the storage device.

Since #3380 the frontend validation blocks applying a disabled storage option, so the UI does not send null anymore, but other clients still can. Found while answering #3380 (comment).

Testing

  • New tests/test_storage_device.py: null, empty and missing storage_device are refused, a given device is used, and input_required() returns 'custom-path', False and 0 but refuses a missing key, None and ''. Against the previous config.py these fail with AttributeError, KeyError, ValueError not raised and the missing input_required.
  • Full suite in a Linux container (python:3.12-bookworm): 235 passed, 176 subtests passed.
  • End-to-end in Docker with Playwright: a camera stores a real partition (loop device, ext4) as storage device; applying a name change with the request rewritten to "storage_device": null returns 500, logs ERROR: Setting "storage_device" must be set, and leaves the camera config unchanged (storage device and name).
  • ruff check / ruff format --check pass.

🤖 Generated with Claude Code

@MichaIng

Copy link
Copy Markdown
Member

Is the frontend validation not supposed to prevent to apply settings, if a disabled storage option is selected? That should be the case at least.

With that, I actually prefer the backend to throw an error, instead of just silently keeping the old storage, as obviously an invalid storage option was sent.

If we want to handle null/None values in the backend, where actual values are expected, then with a proper validator, instead of on per-key basis. But IMO this should be prevented in our frontend in the fist place.

Or, to make the backend robust against this None value:

elif data.get('@storage_device', '').startswith('local-disk')

In this particular case, _set_default_motion_camera applies custom-path if the value was not defined.

@JamBalaya56562
JamBalaya56562 force-pushed the fix/storage-device-null branch from a109bb7 to 94afce2 Compare October 12, 2026 21:53
@JamBalaya56562 JamBalaya56562 changed the title fix(config): keep the storage device when the UI sends none fix(config): refuse camera settings without a storage device Oct 12, 2026
@JamBalaya56562

Copy link
Copy Markdown
Contributor Author

Yes, since #3380 the frontend blocks applying a disabled storage option, whether the File Storage section is collapsed or not, so the UI no longer sends null.
Agreed on raising an error: null, empty or missing storage_device is now refused with a ValueError, like the other input checks in motion_camera_ui_to_dict(). Before, null crashed with AttributeError, a missing field with KeyError, and an empty string was stored silently.
.get('@storage_device', '') would not catch null, as get() returns the stored None.
The handler still answers any ValueError with HTTP 500; returning 400 belongs to the generic validator you mentioned.

@MichaIng

Copy link
Copy Markdown
Member

That should be fine. Though, the check at the top of the function looks very stray there. I suggest one of two things:

  • Move it to the code below where data.get('@storage_device', '').startswith('local-disk') is called, so all checks related to this setting are in one place.
  • Make a "must be set" validator that is called where '@storage_device': ui['storage_device'], is assigned to the data dictionary, an can be generally used for other expected keys in the future.

@MichaIng MichaIng added this to the v0.45.0 milestone Oct 13, 2026
motion_camera_ui_to_dict() raised an AttributeError on None.startswith()
for "storage_device": null and a KeyError when the field was missing,
and silently stored an empty storage device. The UI no longer sends null
for a disabled storage option since the frontend validation blocks it,
but other clients still can.

Add input_required(), which returns a set value and raises a ValueError
for a missing key, None or '' (False and 0 count as set), and use it
where the storage device is put into the camera config.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@JamBalaya56562
JamBalaya56562 force-pushed the fix/storage-device-null branch from 94afce2 to 7fdf8db Compare October 13, 2026 17:14
@JamBalaya56562

Copy link
Copy Markdown
Contributor Author

Went with the second option: a new input_required(ui, key) next to input_sanity_check() raises ValueError('Setting "<key>" must be set') for a missing key, None or '', and returns the value otherwise (False/0 count as set), so it can serve other required keys too.
It is called where '@storage_device' is put into data; the stray check at the top is gone.
Moving the check down to the local-disk branch would not catch a missing field: building data raises KeyError: 'storage_device' first (tried it).
E2E: posting "storage_device": null now logs Setting "storage_device" must be set and leaves the camera config unchanged.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Development

Successfully merging this pull request may close these issues.

2 participants