Skip to content

download_image() returns 401 on an otherwise working session, and the error body is written as the .jpg #189

Open

Description

@GlenSykes

Hi, with the assistance of Claude Code, I've created this Bug report. Thanks for your efforts with this code :)

vsure: 2.10.0
Seen via: Home Assistant 2026.7.4 (Container), verisure integration
Hardware: two Verisure SmartCams

The problem

Capturing a SmartCam image works. Downloading it does not. The download returns
401 Unauthorized, and the integration writes the response body straight to
/config/<mediaId>.jpg without checking the status code. camera_image() then
serves that file, so the camera entity returns a 17-byte "image":

$ # via /api/camera_proxy/camera.<name>
len:  17
hex:  34303120556e617574686f72697a65640a
repr: b'401 Unauthorized\n'

In the UI this looks like a broken thumbnail, and the download button in the
more-info dialog produces a file containing the text 401 Unauthorized.

Because self._image_id is then set to that media id, check_imagelist() logs
"The image is the same" on every subsequent call and never retries, so the
camera stays permanently broken until the integration is reloaded.

What works

The same session authenticates fine for everything else. Data updates succeed
every 60 seconds, continuously:

DEBUG [homeassistant.components.verisure] Finished fetching verisure data in 0.142 seconds (success: True)
DEBUG [verisure] Saved cookies: ['vid', 'vs-access', 'vs-trust-<redacted>', 'vs-refresh']

verisure.capture_smartcam also works. Calling it produces a genuinely new
image on the Verisure side, and the media id increments:

DEBUG [homeassistant.components.verisure] Capturing new image from <device label redacted>
DEBUG [homeassistant.components.verisure] Download new image 44317755233692001
DEBUG [homeassistant.components.verisure] Old image_id=44317755233691001
DEBUG [homeassistant.components.verisure] Deleting old image /config/44317755233691001.jpg

The captured photo is visible in the Verisure mobile app, at the expected
timestamp. So the image exists and the account is permitted to view it.

What is ruled out

  • Not a stale cache. homeassistant.reload_config_entry resets the entity,
    and the retry from Old image_id=None fails identically.
  • Not an expired session. The failure reproduces immediately after a fresh
    login, on a session whose other API calls are succeeding.
  • Not one camera. Both SmartCams fail the same way, on different media ids.
  • Not account permissions. The photo is viewable in the Verisure app.
  • Not an outdated library. vsure 2.10.0 is the newest release.

Steps to reproduce

  1. Call verisure.capture_smartcam on a SmartCam entity.
  2. Wait for the next coordinator poll, then request the camera image
    (/api/camera_proxy/camera.<name>, or just open the more-info dialog).
  3. The response is 17 bytes of 401 Unauthorized, not a JPEG.

Suspected cause

download_image() in vsure's session.py does send the session cookies:

response = requests.get(
    urls.download_image(self._giid, device_label, image_id),
    headers={
        'APPLICATION_ID': 'PS_PYTHON',
        'Accept': 'image/jpeg'},
    cookies=self._request_cookies,
    stream=True)

Since the same cookies work for every other endpoint, the difference is either
the APPLICATION_ID: PS_PYTHON header being rejected on the media endpoint, or
a change to the download URL/auth scheme on Verisure's side. I could not
confirm which from outside the container.

Separate, smaller bug in the same function

Regardless of the 401's cause, download_image() writes the response body to
disk without checking response.status_code. Any error response becomes a
corrupt .jpg. Raising on a non-200 instead would turn this silent corruption
into a visible error, and would stop consumers caching a failure as if it were
an image.

That second part is what makes it permanent downstream: Home Assistant's
camera.py records the media id after the download and then skips every
subsequent fetch as "the image is the same", so one failed download disables
the camera entity until the integration is reloaded. Happy to raise that half
separately against home-assistant/core if you would prefer it split, though
the fix here would prevent it either way.

Activity

  1. persandstrom commented on Aug 17, 2026

    @persandstrom
    Owner

    Hi,

    Thank you for reporting!

    I have changed so that an error is raised if the download commands fail as suggested, however it seems like you do not use the latest python-verisure, i do not recognize the code snippet:

    response = requests.get(
        urls.download_image(self._giid, device_label, image_id),
        headers={
            'APPLICATION_ID': 'PS_PYTHON',
            'Accept': 'image/jpeg'},
        cookies=self._request_cookies,
        stream=True)
    

    Can you please double check which version you use

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions