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
- Return an array with msgtype, wrapping strings as ['msgtype' => 'text', 'text' => ['content' => $value]].
- Return '' or null when no reply is needed — the SDK answers 'success' itself.
- 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
- Document handler return contracts as array|ResponseInterface|null
- Unit-test handler return shapes per reply mode
- Do not share raw handlers between XML and JSON apps
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
- msgtype cannot be empty.
- Invalid Response content "%s".
- Invalid Response type "%s".
- Failed to get auth_corp_info: %s
- Failed to get access_token: %s
AI-assisted analysis of w7corp/easywechat@f0cf0a8b83 (2026-08-21).
Data as JSON: /api/errors/4874a375b121f05a.
Report an issue: GitHub.