Skip to content

415 Unsupported Media Type for projects.delete function in _operations.py #376

@ovidsec

Description

@ovidsec

Using the example python for the Delete an Existing Project API call results in a 415 Unsupported Media Type error
(https://docs.digitalocean.com/reference/api/api-reference/#operation/projects_delete) e.g.:

resp = client.projects.delete(project_id="038ea76a-cae8-4c26-a1c6-3476e7a94ceb")

As shown in the following error output:

╰─λ python digitalOceanDroplet.py 
Request:  <HttpRequest [DELETE], url: 'https://api.digitalocean.com/v2/projects/038ea76a-cae8-4c26-a1c6-3476e7a94ceb'>
Current Headers:  {'Accept': 'application/json'}
Server Response: <HttpResponse: 415 Unsupported Media Type, Content-Type: application/json; charset=utf-8>
Traceback (most recent call last):
... ... ... ...
  File "digitalOceanDroplet.py", line 48, in deleteDOProject
    resp = client.projects.delete(project_id="038ea76a-cae8-4c26-a1c6-3476e7a94ceb")
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File ".../lib/python3.12/site-packages/azure/core/tracing/decorator.py", line 94, in wrapper_use_tracer
    return func(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^
  File ".../lib/python3.12/site-packages/pydo/operations/_operations.py", line 145454, in delete
    raise HttpResponseError(response=response)
azure.core.exceptions.HttpResponseError: Operation returned an invalid status 'Unsupported Media Type'

In file _operations.py, the project DELETE functionality is outlined, with the request sent and response handled at
(https://raw.githubusercontent.com/digitalocean/pydo/refs/heads/main/src/pydo/operations/_operations.py):

145439        _request.url = self._client.format_url(_request.url)
... ... ... 
145448        response = pipeline_response.http_response                                                                                    
145449
145450        if response.status_code not in [204, 404, 412]:
145451          if _stream:
145452              response.read()  # Load the body in memory and close the socket
145453          map_error(status_code=response.status_code, response=response, error_map=error_map)  # type: ignore
145454          raise HttpResponseError(response=response)      
... ... ... 

Temporary Workaround

As a hacky workaround, forcing the Content-Type header prior to the request within _operations.py with the following to force the Content-Type HTTP Header allows the API call to successfully process.

... ... ... 
145439         _request.url = self._client.format_url(_request.url)
                      print('Request: ', _request)                                            # <- optional print for visibility
                      print('Current Headers: ', _request.headers)                            # <- optional print for visibility
                      _request.headers['Content-Type'] = 'application/json; charset=utf-8'    # <- Update HTTP header to force Content-Type
                      print('Forced Content-Type addition headers:', _request.headers)        # <- optional print updated headers
145441         _stream = False
145442         pipeline_response: PipelineResponse = (
145443              self._client._pipeline.run(  # pylint: disable=protected-acces144581         
145444                  _request, stream=_stream, **kwargs
145445             )
145446         )
145447         
145448         response = pipeline_response.http_response
                      print('Server Response:', response)                                     # <- optional print for visibility of server response
145450         if response.status_code not in [204, 404, 412]:
145451              if _stream:
145452                 response.read()  # Load the body in memory and close the socket
145453              map_error(status_code=response.status_code, response=response, error_map=error_map)  # type: ignore
145454              raise HttpResponseError(response=response)
... ... ...

204 is returned confirming successful deletion:

[🔴] × python digitalOceanDroplet.py 
Request:  <HttpRequest [DELETE], url: 'https://api.digitalocean.com/v2/projects/038ea76a-cae8-4c26-a1c6-3476e7a94ceb'>
Current Headers:  {'Accept': 'application/json'}
Forced Content-Type addition headers: {'Accept': 'application/json', 'Content-Type': 'application/json; charset=utf-8'}
Server Response: <HttpResponse: 204 No Content, Content-Type: application/json; charset=utf-8>

Content-Type is defined in multiple places throughout the _operations.py file but not the delete projects function; it's likely better defined elsewhere.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions