w7corp/easywechat · error · InvalidArgumentException
The type of `json` must be string or array.
Error message
The type of `json` must be string or array.
What it means
RequestUtil::formatBody() handles the 'json' option: arrays are json_encode()d (with JSON_FORCE_OBJECT for empty arrays, JSON_UNESCAPED_UNICODE otherwise), and if the value is still not a string afterwards it throws InvalidArgumentException('The type of `json` must be string or array.'). Null, resources, or non-array objects are rejected — note a json_encode failure (invalid UTF-8, resources) also leaves a non-string.
Source
Thrown at src/Kernel/HttpClient/RequestUtil.php:143
if (! $contentType) {
$options['headers']['Content-Type'] = 'text/xml';
}
$options['body'] = $options['xml'];
unset($options['xml']);
}
if (array_key_exists('json', $options)) {
if (is_array($options['json'])) {
/** XXX: 微信的 JSON 是比较奇葩的,比如菜单不能把中文 encode 为 unicode */
$options['json'] = json_encode(
$options['json'],
empty($options['json']) ? JSON_FORCE_OBJECT : JSON_UNESCAPED_UNICODE
);
}
if (! is_string($options['json'])) {
throw new InvalidArgumentException('The type of `json` must be string or array.');
}
if (! $contentType) {
$options['headers']['Content-Type'] = 'application/json';
}
$options['body'] = $options['json'];
unset($options['json']);
}
return $options;
}
public static function createDefaultServerRequest(): ServerRequestInterface
{
$psr17Factory = new Psr17Factory;
$creator = new ServerRequestCreator(View on GitHub (pinned to f0cf0a8b83)
Solutions
- Pass either an associative array or an already-encoded JSON string in the 'json' option
- Cast/cast-convert stdClass with json_decode(json_encode($obj), true) or get_object_vars before passing
- Ensure strings inside the array are valid UTF-8 so json_encode cannot return false
Example fix
// before $options = ['json' => $maybeNullPayload]; // after $options = ['json' => $maybeNullPayload ?? []];
Defensive patterns
Strategy: type-guard
Type guard
function isValidJsonOption(mixed $json): bool
{
return is_array($json) || is_string($json);
}
if (array_key_exists('json', $options) && ! isValidJsonOption($options['json'])) {
throw new InvalidArgumentException("json option must be array|string, got ".gettype($options['json']));
} Try / catch
try {
$response = $api->post('/cgi-bin/...', $options);
} catch (\EasyWeChat\Kernel\Exceptions\InvalidArgumentException $e) {
if (str_contains($e->getMessage(), '`json`')) {
$options['json'] = is_object($options['json'])
? json_decode(json_encode($options['json']), true)
: ($options['json'] ?? []);
$response = $api->post('/cgi-bin/...', $options);
}
} Prevention
- Never assign nullable builder output directly to the json option; coalesce to []
- Convert objects to arrays at the boundary instead of relying on the client to accept them
When it happens
Trigger: Sending a request with ['json' => null] (a builder returned null), ['json' => $stdClass] (stdClass is neither array nor string, so it is never encoded), or an array containing resources/invalid UTF-8 that makes json_encode return false.
Common situations: Conditionally-built payloads defaulting to null, swapping an associative array for an object during refactoring, binary/resource values accidentally nested in the payload.
Related errors
- The type of `xml` must be string or array.
- -40012
- The method "%s" is not supported.
- Response body is empty.
- 400
AI-assisted analysis of w7corp/easywechat@f0cf0a8b83 (2026-08-21).
Data as JSON: /api/errors/9d79f695f4c28c19.
Report an issue: GitHub.