w7corp/easywechat · error · InvalidArgumentException

Invalid Response type "%s".

Error message

Invalid Response type "%s".

What it means

In JSON reply mode (Work Server with messageType 'json'), the handler's return value — after invoking non-string callables — must be an array; strings, numbers, booleans and objects throw this InvalidArgumentException naming the actual gettype. Unlike XML mode (RespondXmlMessage), JSON mode never auto-wraps a plain string into a text reply; only empty values take the automatic 'success' no-op path.

Source

Thrown at src/Kernel/Traits/RespondJsonMessage.php:43

    /**
     * @throws InvalidArgumentException
     */
    protected function normalizeJsonResponse(mixed $response): array
    {
        if (! is_string($response) && is_callable($response)) {
            $response = $response();
        }

        if (is_array($response)) {
            if (! isset($response['msgtype'])) {
                throw new InvalidArgumentException('msgtype cannot be empty.');
            }

            return $response;
        }

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

    /**
     * @throws RuntimeException
     */
    protected function createJsonResponse(array $attributes, ?Encryptor $encryptor = null): ResponseInterface
    {
        $jsonStr = json_encode($attributes, JSON_UNESCAPED_UNICODE);

        if (is_string($jsonStr)) {
            return new Response(200, ['Content-Type' => 'application/json'], $encryptor ? $encryptor->encrypt($jsonStr, messageType: $this->messageType) : $jsonStr);
        }

        throw new InvalidArgumentException(
            sprintf('Invalid Response content "%s".', implode(',', $attributes))
        );

View on GitHub (pinned to f0cf0a8b83)

Solutions

  1. Return an array with msgtype, wrapping strings as ['msgtype' => 'text', 'text' => ['content' => $value]].
  2. Return '' or null when no reply is needed — the SDK answers 'success' itself.
  3. Return a PSR-7 ResponseInterface to bypass normalization entirely.

Example fix

// before: plain string in JSON reply mode
return 'accepted';
// after: array reply (or return '' to auto-answer 'success')
return ['msgtype' => 'text', 'text' => ['content' => 'accepted']];
Defensive patterns

Strategy: validation

Validate before calling

if (is_string($reply) || is_numeric($reply)) { $reply = ['msgtype' => 'text', 'text' => ['content' => (string) $reply]]; }
if (!is_array($reply)) { throw new LogicException('JSON-mode handlers must return array/string/ResponseInterface/null'); }

Type guard

function isJsonReplyPayload(mixed $r): bool { return is_array($r) || is_string($r) || is_numeric($r) || $r === null || $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 non-array in JSON mode'); return new \Nyholm\Psr7\Response(200, [], 'success'); } throw $e; }

Prevention

When it happens

Trigger: A Work JSON-mode handler does return 'ok';, return 200;, return true; or returns a DTO/object; anything that is not an array and not empty reaches the throw.

Common situations: One handler shared between OfficialAccount (XML, plain strings fine) and Work (JSON, arrays only); generic middleware handlers returning boolean 'handled' flags; returning the incoming Message object.

Related errors


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