encode/httpx · error · ValueError

Setting encoding after `text` has been accessed is not allow

Error message

Setting encoding after `text` has been accessed is not allowed.

What it means

Raised as `ValueError` by the `encoding` setter when `self._text` already exists. httpx caches the decoded text on first access; once cached, changing the encoding would not retroactively re-decode the cached string, so the setter refuses to silently produce inconsistent state. Set the encoding before any `.text`/`.json()`-style decode.

Source

Thrown at httpx/_models.py:683

            encoding = self.charset_encoding
            if encoding is None or not _is_known_encoding(encoding):
                if isinstance(self.default_encoding, str):
                    encoding = self.default_encoding
                elif hasattr(self, "_content"):
                    encoding = self.default_encoding(self._content)
            self._encoding = encoding or "utf-8"
        return self._encoding

    @encoding.setter
    def encoding(self, value: str) -> None:
        """
        Set the encoding to use for decoding the byte content into text.

        If the `text` attribute has been accessed, attempting to set the
        encoding will throw a ValueError.
        """
        if hasattr(self, "_text"):
            raise ValueError(
                "Setting encoding after `text` has been accessed is not allowed."
            )
        self._encoding = value

    @property
    def charset_encoding(self) -> str | None:
        """
        Return the encoding, as specified by the Content-Type header.
        """
        content_type = self.headers.get("Content-Type")
        if content_type is None:
            return None

        return _parse_content_type_charset(content_type)

    def _get_content_decoder(self) -> ContentDecoder:
        """
        Returns a decoder instance which can be used to decode the raw byte

View on GitHub (pinned to b5addb64f0)

Solutions

  1. Set `response.encoding = '...'` BEFORE the first `response.text` access.
  2. If you must re-decode, delete the cache: `del response._text` (private) then set encoding, or re-request.
  3. Pass `default_encoding=` to the client/request to control autodetection up front instead of mutating post-hoc.
  4. Decode bytes yourself: `response.content.decode('latin-1')` when you need a one-off alternative encoding.

Example fix

// before
print(response.text)
response.encoding = 'latin-1'  # ValueError

// after
response.encoding = 'latin-1'
print(response.text)
Defensive patterns

Strategy: validation

Validate before calling

def can_set_encoding(resp: httpx.Response) -> bool:
    return not hasattr(resp, '_text')

Type guard

import httpx

def encoding_is_locked(resp: httpx.Response) -> bool:
    return hasattr(resp, '_text')

Try / catch

try:
    response.encoding = 'latin-1'
except ValueError:
    del response._text  # invalidate cache, then retry
    response.encoding = 'latin-1'

Prevention

When it happens

Trigger: Calling `response.text` (or anything that calls the `text` property) and then `response.encoding = 'latin-1'`. Also indirectly via helper functions that read `.text` then attempt to override encoding for a retry.

Common situations: Charm/charset-autodetection loops that read text then try to fix the encoding; porting `requests` code that mutated `r.encoding` freely; codecs mismatched against a mislabelled `Content-Type`.

Related errors


AI-assisted analysis of encode/httpx@b5addb64f0 (2026-08-04). Data as JSON: /data/errors/fd76904278088613.json. Report an issue: GitHub.