w7corp/easywechat · error · BadResponseException

Response body is empty.

Error message

Response body is empty.

What it means

Response::toArray() requires a non-empty body: it calls the underlying Symfony response's getContent($throw) and throws BadResponseException('Response body is empty.') when the content is ''. The HTTP exchange completed but returned zero bytes — empty 2xx, an error status with an empty body (when throw=false), or a proxied response with no payload.

Source

Thrown at src/Kernel/HttpClient/Response.php:98

            return (bool) ($this->failureJudge)($this);
        }

        try {
            return $this->getStatusCode() >= 400;
        } catch (Throwable $e) {
            return true;
        }
    }

    /**
     * @throws BadResponseException
     */
    public function toArray(?bool $throw = null): array
    {
        $throw ??= $this->throw;

        if ('' === $content = $this->response->getContent($throw)) {
            throw new BadResponseException('Response body is empty.');
        }

        $contentType = $this->getHeaderLine('content-type', $throw);

        if (str_contains($contentType, 'text/xml')
            || str_contains($contentType, 'application/xml')
            || str_starts_with($content, '<xml>')) {
            try {
                return Xml::parse($content) ?? [];
            } catch (Throwable $e) {
                throw new BadResponseException('Response body is not valid xml.', 400, $e);
            }
        }

        return $this->response->toArray($throw);
    }

    public function toJson(?bool $throw = null): string|false

View on GitHub (pinned to f0cf0a8b83)

Solutions

  1. Check the status code first: $response->getStatusCode() and $response->getContent(false) before parsing
  2. Use saveAs($path) for binary/media downloads instead of toArray()
  3. Log the raw exchange (url, status, headers) when it fires to identify the empty-body source

Example fix

// before
$data = $response->toArray(); // throws when body is empty

// after
if ($response->getStatusCode() !== 200 || '' === $response->getContent(false)) {
    throw new RuntimeException('Unexpected empty response: HTTP '.$response->getStatusCode());
}
$data = $response->toArray();
Defensive patterns

Strategy: validation

Validate before calling

// Guard before parsing
$content = $response->getContent(false);
if ('' === $content) {
    $status = $response->getStatusCode();
    throw new RuntimeException("WeChat returned an empty body (HTTP {$status})");
}
$data = $response->toArray();

Try / catch

try {
    $data = $response->toArray();
} catch (\Symfony\Component\HttpClient\Exception\BadResponseException $e) {
    if (str_contains($e->getMessage(), 'empty')) {
        // empty body: decide per endpoint (retry / treat as error / ignore)
        $data = [];
    } else {
        throw $e;
    }
}

Prevention

When it happens

Trigger: Calling toArray() on a media/binary download response, on a response whose status is 3xx/4xx/5xx with empty body (e.g. after getStatusCode() was not checked), or on a callback verification GET request that WeChat answers with an empty echo body.

Common situations: Using toArray() where saveAs() is needed (downloads), gateways/proxies stripping error bodies, WeChat returning empty body on some rate-limit/refused paths, following redirects to empty endpoints.

Related errors


AI-assisted analysis of w7corp/easywechat@f0cf0a8b83 (2026-08-21). Data as JSON: /api/errors/9eb0af63f2df78bf. Report an issue: GitHub.