w7corp/easywechat · error · InvalidArgumentException

Invalid Response type "%s".

Error message

Invalid Response type "%s".

What it means

XML-mode normalizeResponse() accepts arrays, strings and numerics (strings/numerics become text replies) and throws on everything else, naming gettype in the message. Objects, resources and true from a handler hit this path; null and '' are treated as empty by transformToReply() and answered with the automatic 'success' response, so they never throw.

Source

Thrown at src/Kernel/Traits/RespondXmlMessage.php:78

            foreach ($response as $key => $value) {
                $normalized[(string) $key] = $value;
            }

            if (! isset($normalized['MsgType'])) {
                throw new InvalidArgumentException('MsgType cannot be empty.');
            }

            return $normalized;
        }

        if (is_string($response) || is_numeric($response)) {
            return [
                'MsgType' => 'text',
                'Content' => $response,
            ];
        }

        throw new InvalidArgumentException(
            sprintf('Invalid Response type "%s".', gettype($response))
        );
    }

    /**
     * @param  array<string, mixed>  $attributes
     *
     * @throws RuntimeException
     */
    protected function createXmlResponse(array $attributes, ?Encryptor $encryptor = null): ResponseInterface
    {
        $xml = Xml::build($attributes);

        return new Response(200, ['Content-Type' => 'application/xml'], $encryptor ? $encryptor->encrypt($xml) : $xml);
    }
}

View on GitHub (pinned to f0cf0a8b83)

Solutions

  1. Return a string, an array, or a PSR ResponseInterface from reply handlers.
  2. For 'handled, no reply', return null or '' — the SDK replies 'success'.
  3. Cast or restructure boolean-returning helpers so the reply chain yields a supported type.

Example fix

// before: boolean return
public function __invoke($message, $next) { $this->process($message); return true; }
// after: empty return means the automatic 'success' no-op reply
public function __invoke($message, $next) { $this->process($message); return ''; }
Defensive patterns

Strategy: validation

Validate before calling

if (is_object($reply) || is_bool($reply) || is_resource($reply)) { $reply = ''; }

Type guard

function isAcceptableXmlReply(mixed $r): bool { return $r === null || $r === '' || is_string($r) || is_numeric($r) || is_array($r) || $r instanceof \Psr\Http\Message\ResponseInterface; }

Try / catch

try { return $server->serve(); } catch (\EasyWeChat\Kernel\Exceptions\InvalidArgumentException $e) { if (str_contains($e->getMessage(), 'Invalid Response type')) { \Log::warning('handler returned unsupported reply type'); return new \Nyholm\Psr7\Response(200, [], 'success'); } throw $e; }

Prevention

When it happens

Trigger: A handler returns true (a 'handled' flag), an object such as the incoming Message instance, a DTO/ArrayObject, or a resource; passthrough handlers doing return $message;.

Common situations: Middleware pipelines whose handlers return booleans; returning Laravel models or value objects from business code; returning the result of a void-ish API that is an object.

Related errors


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