[GH-ISSUE #96] KeyError instead of adequate exception on try to delete a running virtual machine. #47

Closed
opened 2026-02-27 15:46:10 +03:00 by kerem · 4 comments
Owner

Originally created by @ImHereByChance on GitHub (Mar 30, 2022).
Original GitHub issue: https://github.com/proxmoxer/proxmoxer/issues/96

Originally assigned to: @jhollowe on GitHub.

After update to 1.3.0, when I try to delete a running virtual machine using HTTP bacekend, I get a KeyError exception instead of proxmoxer.core.ResourceException as it was in 1.2.0.

Traceback (most recent call last):
  File "/home/user/.cache/pypoetry/virtualenvs/vm-broker-pzDY6P43-py3.8/lib/python3.8/site-packages/uvicorn/protocols/http/httptools_impl.py", line 390, in run_asgi
    result = await app(self.scope, self.receive, self.send)
  File "/home/user/.cache/pypoetry/virtualenvs/vm-broker-pzDY6P43-py3.8/lib/python3.8/site-packages/uvicorn/middleware/proxy_headers.py", line 45, in __call__
    return await self.app(scope, receive, send)
  File "/home/user/.cache/pypoetry/virtualenvs/vm-broker-pzDY6P43-py3.8/lib/python3.8/site-packages/fastapi/applications.py", line 190, in __call__
    await super().__call__(scope, receive, send)
  File "/home/user/.cache/pypoetry/virtualenvs/vm-broker-pzDY6P43-py3.8/lib/python3.8/site-packages/starlette/applications.py", line 111, in __call__
    await self.middleware_stack(scope, receive, send)
  File "/home/user/.cache/pypoetry/virtualenvs/vm-broker-pzDY6P43-py3.8/lib/python3.8/site-packages/starlette/middleware/errors.py", line 181, in __call__
    raise exc from None
  File "/home/user/.cache/pypoetry/virtualenvs/vm-broker-pzDY6P43-py3.8/lib/python3.8/site-packages/starlette/middleware/errors.py", line 159, in __call__
    await self.app(scope, receive, _send)
  File "/home/user/.cache/pypoetry/virtualenvs/vm-broker-pzDY6P43-py3.8/lib/python3.8/site-packages/starlette/middleware/cors.py", line 86, in __call__
    await self.simple_response(scope, receive, send, request_headers=headers)
  File "/home/user/.cache/pypoetry/virtualenvs/vm-broker-pzDY6P43-py3.8/lib/python3.8/site-packages/starlette/middleware/cors.py", line 142, in simple_response
    await self.app(scope, receive, send)
  File "/home/user/.cache/pypoetry/virtualenvs/vm-broker-pzDY6P43-py3.8/lib/python3.8/site-packages/starlette/exceptions.py", line 82, in __call__
    raise exc from None
  File "/home/user/.cache/pypoetry/virtualenvs/vm-broker-pzDY6P43-py3.8/lib/python3.8/site-packages/starlette/exceptions.py", line 71, in __call__
    await self.app(scope, receive, sender)
  File "/home/user/.cache/pypoetry/virtualenvs/vm-broker-pzDY6P43-py3.8/lib/python3.8/site-packages/starlette/routing.py", line 566, in __call__
    await route.handle(scope, receive, send)
  File "/home/user/.cache/pypoetry/virtualenvs/vm-broker-pzDY6P43-py3.8/lib/python3.8/site-packages/starlette/routing.py", line 227, in handle
    await self.app(scope, receive, send)
  File "/home/user/.cache/pypoetry/virtualenvs/vm-broker-pzDY6P43-py3.8/lib/python3.8/site-packages/starlette/routing.py", line 41, in app
    response = await func(request)
  File "/home/user/.cache/pypoetry/virtualenvs/vm-broker-pzDY6P43-py3.8/lib/python3.8/site-packages/fastapi/routing.py", line 188, in app
    raw_response = await run_endpoint_function(
  File "/home/user/.cache/pypoetry/virtualenvs/vm-broker-pzDY6P43-py3.8/lib/python3.8/site-packages/fastapi/routing.py", line 135, in run_endpoint_function
    return await dependant.call(**values)
  File "/home/user/Projects/bigdata/vm_broker/src/vm_broker/api/v1/vm/views.py", line 227, in delete_vm_view
    await delete_vm(vm_uuid, proxmox, mongo, force_stop)
  File "/home/user/Projects/bigdata/vm_broker/src/vm_broker/api/v1/vm/core.py", line 86, in delete_vm
    px.delete_vm(
  File "/home/user/Projects/bigdata/vm_broker/src/vm_broker/external/proxmox/utils.py", line 86, in delete_vm
    node.qemu(vmid).delete(purge=1)
  File "/home/user/.cache/pypoetry/virtualenvs/vm-broker-pzDY6P43-py3.8/lib/python3.8/site-packages/proxmoxer/core.py", line 152, in delete
    return self(args)._request("DELETE", params=params)
  File "/home/user/.cache/pypoetry/virtualenvs/vm-broker-pzDY6P43-py3.8/lib/python3.8/site-packages/proxmoxer/core.py", line 129, in _request
    errors=(self._store["serializer"].loads_errors(resp) or {}),
  File "/home/user/.cache/pypoetry/virtualenvs/vm-broker-pzDY6P43-py3.8/lib/python3.8/site-packages/proxmoxer/backends/https.py", line 180, in loads_errors
    return json.loads(response.text)["errors"]
KeyError: 'errors'

it occurs here

class JsonSerializer(object):
    content_types = [
        "application/json",
        "application/x-javascript",
        "text/javascript",
        "text/x-javascript",
        "text/x-json",
    ]

    def get_accept_types(self):
        return ", ".join(self.content_types)

    def loads(self, response):
        try:
            return json.loads(response.content.decode("utf-8"))["data"]
        except (UnicodeDecodeError, ValueError):
            return {"errors": response.content}

    def loads_errors(self, response):
        try:
            return json.loads(response.text)["errors"]    #  <======= here
        except (UnicodeDecodeError, ValueError):
            return {"errors": response.content}

because of response.text == '{"data":null}' and there is no "errors" key in response payload.

Originally created by @ImHereByChance on GitHub (Mar 30, 2022). Original GitHub issue: https://github.com/proxmoxer/proxmoxer/issues/96 Originally assigned to: @jhollowe on GitHub. After update to 1.3.0, when I try to delete a running virtual machine using HTTP bacekend, I get a `KeyError` exception instead of `proxmoxer.core.ResourceException` as it was in 1.2.0. ``` Traceback (most recent call last): File "/home/user/.cache/pypoetry/virtualenvs/vm-broker-pzDY6P43-py3.8/lib/python3.8/site-packages/uvicorn/protocols/http/httptools_impl.py", line 390, in run_asgi result = await app(self.scope, self.receive, self.send) File "/home/user/.cache/pypoetry/virtualenvs/vm-broker-pzDY6P43-py3.8/lib/python3.8/site-packages/uvicorn/middleware/proxy_headers.py", line 45, in __call__ return await self.app(scope, receive, send) File "/home/user/.cache/pypoetry/virtualenvs/vm-broker-pzDY6P43-py3.8/lib/python3.8/site-packages/fastapi/applications.py", line 190, in __call__ await super().__call__(scope, receive, send) File "/home/user/.cache/pypoetry/virtualenvs/vm-broker-pzDY6P43-py3.8/lib/python3.8/site-packages/starlette/applications.py", line 111, in __call__ await self.middleware_stack(scope, receive, send) File "/home/user/.cache/pypoetry/virtualenvs/vm-broker-pzDY6P43-py3.8/lib/python3.8/site-packages/starlette/middleware/errors.py", line 181, in __call__ raise exc from None File "/home/user/.cache/pypoetry/virtualenvs/vm-broker-pzDY6P43-py3.8/lib/python3.8/site-packages/starlette/middleware/errors.py", line 159, in __call__ await self.app(scope, receive, _send) File "/home/user/.cache/pypoetry/virtualenvs/vm-broker-pzDY6P43-py3.8/lib/python3.8/site-packages/starlette/middleware/cors.py", line 86, in __call__ await self.simple_response(scope, receive, send, request_headers=headers) File "/home/user/.cache/pypoetry/virtualenvs/vm-broker-pzDY6P43-py3.8/lib/python3.8/site-packages/starlette/middleware/cors.py", line 142, in simple_response await self.app(scope, receive, send) File "/home/user/.cache/pypoetry/virtualenvs/vm-broker-pzDY6P43-py3.8/lib/python3.8/site-packages/starlette/exceptions.py", line 82, in __call__ raise exc from None File "/home/user/.cache/pypoetry/virtualenvs/vm-broker-pzDY6P43-py3.8/lib/python3.8/site-packages/starlette/exceptions.py", line 71, in __call__ await self.app(scope, receive, sender) File "/home/user/.cache/pypoetry/virtualenvs/vm-broker-pzDY6P43-py3.8/lib/python3.8/site-packages/starlette/routing.py", line 566, in __call__ await route.handle(scope, receive, send) File "/home/user/.cache/pypoetry/virtualenvs/vm-broker-pzDY6P43-py3.8/lib/python3.8/site-packages/starlette/routing.py", line 227, in handle await self.app(scope, receive, send) File "/home/user/.cache/pypoetry/virtualenvs/vm-broker-pzDY6P43-py3.8/lib/python3.8/site-packages/starlette/routing.py", line 41, in app response = await func(request) File "/home/user/.cache/pypoetry/virtualenvs/vm-broker-pzDY6P43-py3.8/lib/python3.8/site-packages/fastapi/routing.py", line 188, in app raw_response = await run_endpoint_function( File "/home/user/.cache/pypoetry/virtualenvs/vm-broker-pzDY6P43-py3.8/lib/python3.8/site-packages/fastapi/routing.py", line 135, in run_endpoint_function return await dependant.call(**values) File "/home/user/Projects/bigdata/vm_broker/src/vm_broker/api/v1/vm/views.py", line 227, in delete_vm_view await delete_vm(vm_uuid, proxmox, mongo, force_stop) File "/home/user/Projects/bigdata/vm_broker/src/vm_broker/api/v1/vm/core.py", line 86, in delete_vm px.delete_vm( File "/home/user/Projects/bigdata/vm_broker/src/vm_broker/external/proxmox/utils.py", line 86, in delete_vm node.qemu(vmid).delete(purge=1) File "/home/user/.cache/pypoetry/virtualenvs/vm-broker-pzDY6P43-py3.8/lib/python3.8/site-packages/proxmoxer/core.py", line 152, in delete return self(args)._request("DELETE", params=params) File "/home/user/.cache/pypoetry/virtualenvs/vm-broker-pzDY6P43-py3.8/lib/python3.8/site-packages/proxmoxer/core.py", line 129, in _request errors=(self._store["serializer"].loads_errors(resp) or {}), File "/home/user/.cache/pypoetry/virtualenvs/vm-broker-pzDY6P43-py3.8/lib/python3.8/site-packages/proxmoxer/backends/https.py", line 180, in loads_errors return json.loads(response.text)["errors"] KeyError: 'errors' ``` it occurs [here](https://github.com/proxmoxer/proxmoxer/blob/5dc29627fa7f321d467b2a97068db6daf7e3a159/proxmoxer/backends/https.py#L181) ``` class JsonSerializer(object): content_types = [ "application/json", "application/x-javascript", "text/javascript", "text/x-javascript", "text/x-json", ] def get_accept_types(self): return ", ".join(self.content_types) def loads(self, response): try: return json.loads(response.content.decode("utf-8"))["data"] except (UnicodeDecodeError, ValueError): return {"errors": response.content} def loads_errors(self, response): try: return json.loads(response.text)["errors"] # <======= here except (UnicodeDecodeError, ValueError): return {"errors": response.content} ``` because of `response.text == '{"data":null}'` and there is no `"errors"` key in response payload.
Author
Owner

@meffie commented on GitHub (Apr 4, 2022):

I see the same after upgrading to 1.3.0. Going to pin versions to 1.2.0 for the time being.

<!-- gh-comment-id:1087908981 --> @meffie commented on GitHub (Apr 4, 2022): I see the same after upgrading to 1.3.0. Going to pin versions to 1.2.0 for the time being.
Author
Owner

@r3d07 commented on GitHub (Apr 26, 2022):

This is not unique to deleting a running VM. For example, I have this issue when issues a qemu agent ping to a host that is not yet booted. Looking at the code, it appears that this is regression where the error field was expected to be there if a command is not successful -- something that is not necessarily the case.

<!-- gh-comment-id:1109205052 --> @r3d07 commented on GitHub (Apr 26, 2022): This is not unique to deleting a running VM. For example, I have this issue when issues a `qemu agent ping` to a host that is not yet booted. Looking at the code, it appears that this is regression where the `error` field was expected to be there if a command is not successful -- something that is not necessarily the case.
Author
Owner

@TimoteusRuotsalainen commented on GitHub (May 10, 2022):

The error message is stated in response.reason

response.__dict__
{'_content': b'{"data":null}', '_content_consumed': True, '_next': None, 'status_code': 500, 'headers': {'Cache-Control': 'max-age=0', 'Connection': 'close', 'Date': 'Tue, 10 May 2022 20:49:45 GMT', 'Pragma': 'no-cache', 'Server': 'pve-api-daemon/3.0', 'Content-Length': '13', 'Content-Type': 'application/json;charset=UTF-8', 'Expires': 'Tue, 10 May 2022 20:49:45 GMT'}, 'raw': <urllib3.response.HTTPResponse object at 0x000001B23355D2A0>, 'url': 'https://<ip>:8006/api2/json/nodes/<node-name>/lxc', 'encoding': 'UTF-8', 'history': [], 'reason': 'Only root can pass arbitrary filesystem paths. at /usr/share/perl5/PVE/Storage.pm line 492.', 'cookies': <RequestsCookieJar[]>, 'elapsed': datetime.timedelta(microseconds=34646), 'request': <PreparedRequest [POST]>, 'connection': <requests.adapters.HTTPAdapter object at 0x000001B232E41930>}

Adding a print debug helped to show the actual error messages

    def loads_errors(self, response):
        try:
            print(response.reason)
            return json.loads(response.text)["errors"]
        except (UnicodeDecodeError, ValueError):
            return {"errors": response.content}
<!-- gh-comment-id:1122859244 --> @TimoteusRuotsalainen commented on GitHub (May 10, 2022): The error message is stated in response.reason ``` response.__dict__ {'_content': b'{"data":null}', '_content_consumed': True, '_next': None, 'status_code': 500, 'headers': {'Cache-Control': 'max-age=0', 'Connection': 'close', 'Date': 'Tue, 10 May 2022 20:49:45 GMT', 'Pragma': 'no-cache', 'Server': 'pve-api-daemon/3.0', 'Content-Length': '13', 'Content-Type': 'application/json;charset=UTF-8', 'Expires': 'Tue, 10 May 2022 20:49:45 GMT'}, 'raw': <urllib3.response.HTTPResponse object at 0x000001B23355D2A0>, 'url': 'https://<ip>:8006/api2/json/nodes/<node-name>/lxc', 'encoding': 'UTF-8', 'history': [], 'reason': 'Only root can pass arbitrary filesystem paths. at /usr/share/perl5/PVE/Storage.pm line 492.', 'cookies': <RequestsCookieJar[]>, 'elapsed': datetime.timedelta(microseconds=34646), 'request': <PreparedRequest [POST]>, 'connection': <requests.adapters.HTTPAdapter object at 0x000001B232E41930>} ``` Adding a print debug helped to show the actual error messages ``` def loads_errors(self, response): try: print(response.reason) return json.loads(response.text)["errors"] except (UnicodeDecodeError, ValueError): return {"errors": response.content} ```
Author
Owner

@jhollowe commented on GitHub (May 14, 2022):

Version 1.3.1 is now available in PyPi

<!-- gh-comment-id:1126740389 --> @jhollowe commented on GitHub (May 14, 2022): Version 1.3.1 is now available in PyPi
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
starred/proxmoxer#47
No description provided.