{"record":{"id":"9d79f695f4c28c19","repo":"w7corp/easywechat","slug":"the-type-of-json-must-be-string-or-array","errorCode":null,"errorMessage":"The type of `json` must be string or array.","messagePattern":"The type of `json` must be string or array\\.","errorType":"exception","errorClass":"InvalidArgumentException","httpStatus":null,"severity":"error","filePath":"src/Kernel/HttpClient/RequestUtil.php","lineNumber":143,"sourceCode":"            if (! $contentType) {\n                $options['headers']['Content-Type'] = 'text/xml';\n            }\n\n            $options['body'] = $options['xml'];\n            unset($options['xml']);\n        }\n\n        if (array_key_exists('json', $options)) {\n            if (is_array($options['json'])) {\n                /** XXX: 微信的 JSON 是比较奇葩的，比如菜单不能把中文 encode 为 unicode */\n                $options['json'] = json_encode(\n                    $options['json'],\n                    empty($options['json']) ? JSON_FORCE_OBJECT : JSON_UNESCAPED_UNICODE\n                );\n            }\n\n            if (! is_string($options['json'])) {\n                throw new InvalidArgumentException('The type of `json` must be string or array.');\n            }\n\n            if (! $contentType) {\n                $options['headers']['Content-Type'] = 'application/json';\n            }\n\n            $options['body'] = $options['json'];\n            unset($options['json']);\n        }\n\n        return $options;\n    }\n\n    public static function createDefaultServerRequest(): ServerRequestInterface\n    {\n        $psr17Factory = new Psr17Factory;\n\n        $creator = new ServerRequestCreator(","sourceCodeStart":125,"sourceCodeEnd":161,"githubUrl":"https://github.com/w7corp/easywechat/blob/f0cf0a8b8361417ed683b8246d0ecbaf0aafcaa8/src/Kernel/HttpClient/RequestUtil.php#L125-L161","documentation":"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.","triggerScenarios":"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.","commonSituations":"Conditionally-built payloads defaulting to null, swapping an associative array for an object during refactoring, binary/resource values accidentally nested in the payload.","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"],"exampleFix":"// before\n$options = ['json' => $maybeNullPayload];\n\n// after\n$options = ['json' => $maybeNullPayload ?? []];","handlingStrategy":"type-guard","validationCode":null,"typeGuard":"function isValidJsonOption(mixed $json): bool\n{\n    return is_array($json) || is_string($json);\n}\n\nif (array_key_exists('json', $options) && ! isValidJsonOption($options['json'])) {\n    throw new InvalidArgumentException(\"json option must be array|string, got \".gettype($options['json']));\n}","tryCatchPattern":"try {\n    $response = $api->post('/cgi-bin/...', $options);\n} catch (\\EasyWeChat\\Kernel\\Exceptions\\InvalidArgumentException $e) {\n    if (str_contains($e->getMessage(), '`json`')) {\n        $options['json'] = is_object($options['json'])\n            ? json_decode(json_encode($options['json']), true)\n            : ($options['json'] ?? []);\n        $response = $api->post('/cgi-bin/...', $options);\n    }\n}","preventionTips":["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"],"tags":["php","easywechat","http-client","json","request-body"],"backgroundTag":"invalid-request-body","analyzedSha":"f0cf0a8b8361417ed683b8246d0ecbaf0aafcaa8","analyzedAt":"2026-08-21T05:29:19.565Z","schemaVersion":2},"datasetVersion":"2026-08-21T11:28:35.574Z"}