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

  1. Pass either an associative array or an already-encoded JSON string in the 'json' option
  2. Cast/cast-convert stdClass with json_decode(json_encode($obj), true) or get_object_vars before passing
  3. 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

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


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