Skip to content

Python runtime can double-decode compressed responses #177

Description

@hardbyte

Summary

reflectapi-runtime can double-decode compressed responses in the Python runtime request path.

When the transport layer returns a response body that has already been decompressed, but the response headers still include Content-Encoding: gzip (or another compression encoding), _make_request rebuilds an httpx.Response with those headers and that already-decoded body. httpx.Response(..., content=...) then attempts to decode the body again and raises a decoding error before the generated client can deserialize the API response.

Observed error

httpx.DecodingError: Error -3 while decompressing data: incorrect header check
...
reflectapi_runtime.exceptions.NetworkError: Network error: Error -3 while decompressing data: incorrect header check

Why this appears to happen

In reflectapi_runtime.client, _make_request builds a new httpx.Response from the structural response:

parsed_response = httpx.Response(
    status_code=client_response.status,
    headers=client_response.headers,
    content=client_response.body,
)

If client_response.body is already decoded but client_response.headers still contains Content-Encoding: gzip, the reconstructed httpx.Response treats the decoded JSON bytes as gzip bytes and fails.

Expected behavior

The runtime should successfully validate/deserialise the response regardless of whether the underlying transport has already decompressed it.

Possible fixes:

  • strip Content-Encoding / related compression headers when rebuilding an httpx.Response from an already-decoded structural body
  • preserve the raw compressed body when preserving compression headers
  • avoid constructing a second decoding httpx.Response for validation

Workaround

Passing Accept-Encoding: identity in the generated client headers avoids the issue by preventing compressed responses:

client = AsyncClient(base_url, headers={"Accept-Encoding": "identity"})

That is functional, but it is surprising and loses compression for all requests.

Environment

  • reflectapi-runtime: observed with 0.17.6
  • Python generated client using AsyncClient
  • Server response: JSON response with compression enabled

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