Repository: home-assistant/core
File: homeassistant/helpers/config_entry_oauth2_flow.py (Python)

Code before:
            {
                "grant_type": "refresh_token",
                "client_id": self.client_id,
                "refresh_token": token["refresh_token"],
            }
        )

        return {**token, **new_token}

    async def _token_request(self, data: dict) -> dict:
        """Make a token request.

        Raises OAuth2TokenRequestError on token request failure.
        """
        session = async_get_clientsession(self.hass)

        data["client_id"] = self.client_id
        if self.client_secret:
            data["client_secret"] = self.client_secret

        _LOGGER.debug("Sending token request to %s", self.token_url)

        try:
            resp = await session.post(self.token_url, data=data)
            if resp.status >= 400:
                try:
                    error_response = await resp.json()
                except (ClientError, JSONDecodeError):
                    error_response = {}
                error_code = error_response.get("error", "unknown")
                error_description = error_response.get(
                    "error_description", "unknown error"
                )
                _LOGGER.error(
                    "Token request for %s failed (%s): %s",
                    self.domain,
                    error_code,
                    error_description,
                )
            resp.raise_for_status()
        except ClientResponseError as err:
            report_usage(
                "is using the `OAuth2 config entry helper` without handling `OAuth2TokenRequestError`. "
                "Please update your integration to handle `OAuth2TokenRequestError` gracefully "
                "(see https://developers.home-assistant.io).",
                breaks_in_ha_version="2026.8",
                core_behavior=ReportBehavior.LOG,
                integration_domain=self._domain,
            )

            if err.status == HTTPStatus.TOO_MANY_REQUESTS or 500 <= err.status <= 599:

Review comment (suggestion):
Side note: In a follow up PR, I think we should just get the body with `resp.text` and include that in the log. Some servers don't return JSON data here so it's more compatible to just include the body as text. The fitbit integration has an example of this.

Code after:
                "grant_type": "refresh_token",
                "client_id": self.client_id,
                "refresh_token": token["refresh_token"],
            }
        )

        return {**token, **new_token}

    async def _token_request(self, data: dict) -> dict:
        """Make a token request.

        Raises OAuth2TokenRequestError on token request failure.
        """
        session = async_get_clientsession(self.hass)

        data["client_id"] = self.client_id
        if self.client_secret:
            data["client_secret"] = self.client_secret

        _LOGGER.debug("Sending token request to %s", self.token_url)

        try:
            resp = await session.post(self.token_url, data=data)
            if resp.status >= 400:
                try:
                    error_response = await resp.json()
                except ClientError, JSONDecodeError:
                    error_response = {}
                error_code = error_response.get("error", "unknown")
                error_description = error_response.get(
                    "error_description", "unknown error"
                )
                _LOGGER.error(
                    "Token request for %s failed (%s): %s",
                    self.domain,
                    error_code,
                    error_description,
                )
            resp.raise_for_status()
        except ClientResponseError as err:
            if err.status == HTTPStatus.TOO_MANY_REQUESTS or 500 <= err.status <= 599:
                # Recoverable error
                raise OAuth2TokenRequestTransientError(
                    request_info=err.request_info,
                    history=err.history,
                    status=err.status,
                    message=err.message,
                    headers=err.headers,
                    domain=self._domain,
                ) from err
            if 400 <= err.status <= 499: