{"record":{"id":"608a09f25656eb61","repo":"w7corp/easywechat","slug":"40012","errorCode":"-40012","errorMessage":"Invalid json data.","messagePattern":"Invalid json data\\.","errorType":"exception","errorClass":"RuntimeException","httpStatus":null,"severity":"error","filePath":"src/Kernel/Encryptor.php","lineNumber":129,"sourceCode":"\n        return Xml::build($response);\n    }\n\n    public function encryptAsJson(string $plaintext, ?string $nonce = null, int|string|null $timestamp = null): string\n    {\n        $encrypted = $this->encryptAsArray($plaintext, $nonce, $timestamp);\n\n        $response = [\n            'encrypt' => $encrypted['ciphertext'],\n            'msgsignature' => $encrypted['signature'],\n            'timestamp' => $encrypted['timestamp'],\n            'nonce' => $encrypted['nonce'],\n        ];\n\n        $jsonStr = json_encode($response, JSON_UNESCAPED_UNICODE);\n\n        if ($jsonStr === false) {\n            throw new RuntimeException('Invalid json data.', self::ERROR_JSON_BUILD);\n        }\n\n        return $jsonStr;\n    }\n\n    /**\n     * @throws RuntimeException\n     */\n    public function encryptAsArray(string $plaintext, ?string $nonce = null, int|string|null $timestamp = null): array\n    {\n        try {\n            $plaintext = Pkcs7::padding(\n                random_bytes(self::BLOCK_SIZE).pack('N', strlen($plaintext)).$plaintext.$this->appId,\n                blockSize: strlen($this->aesKey)\n            );\n            $ciphertext = base64_encode(\n                openssl_encrypt(\n                    $plaintext,","sourceCodeStart":111,"sourceCodeEnd":147,"githubUrl":"https://github.com/w7corp/easywechat/blob/f0cf0a8b8361417ed683b8246d0ecbaf0aafcaa8/src/Kernel/Encryptor.php#L111-L147","documentation":"Encryptor::encryptAsJson() assembles the {encrypt, msgsignature, timestamp, nonce} reply envelope and json_encode()s it; if encoding fails (returns false) it throws RuntimeException('Invalid json data.', -40012 ERROR_JSON_BUILD). json_encode only fails on data it cannot serialize — practically, invalid UTF-8 byte sequences in the plaintext that ends up inside the encrypted payload envelope values.","triggerScenarios":"Calling $encryptor->encrypt($plaintext, $nonce, $timestamp, 'json') (JSON message type, used by WeChat Work callbacks) where $plaintext contains binary or broken-encoded user content (e.g. strings from a latin1 DB column or truncated multibyte input).","commonSituations":"Mixing encodings (database connection not utf8mb4), user nicknames/remarks with 4-byte emoji mangled by a non-UTF-8 pipeline, binary data accidentally passed as the message body.","solutions":["Sanitize/convert the plaintext to valid UTF-8 before encrypting: mb_convert_encoding($plaintext, 'UTF-8', 'UTF-8') drops invalid sequences","Check json_last_error() on your own payload if you build the plaintext JSON yourself before encryption"],"exampleFix":"// before\n$reply = $encryptor->encrypt($messageWithBrokenUtf8, null, null, 'json');\n\n// after\n$clean = mb_convert_encoding($messageWithBrokenUtf8, 'UTF-8', 'UTF-8');\n$reply = $encryptor->encrypt($clean, null, null, 'json');","handlingStrategy":"validation","validationCode":"// Reject invalid UTF-8 before encrypting JSON replies\n$clean = mb_convert_encoding($plaintext, 'UTF-8', 'UTF-8');\nif ($clean === null || !mb_check_encoding($clean, 'UTF-8')) {\n    throw new InvalidArgumentException('Plaintext must be valid UTF-8');\n}\n$reply = $encryptor->encrypt($clean, null, null, 'json');","typeGuard":null,"tryCatchPattern":"try {\n    $json = $encryptor->encryptAsJson($plaintext);\n} catch (\\EasyWeChat\\Kernel\\Exceptions\\RuntimeException $e) {\n    if ($e->getCode() === \\EasyWeChat\\Kernel\\Encryptor::ERROR_JSON_BUILD) {\n        // sanitize and retry once\n        $json = $encryptor->encryptAsJson(mb_convert_encoding($plaintext, 'UTF-8', 'UTF-8'));\n    } else {\n        throw $e;\n    }\n}","preventionTips":["Keep the whole pipeline (DB connections, templates) utf8mb4 so plaintext is always valid UTF-8","When building JSON plaintext yourself, check json_last_error() before passing it to the encryptor"],"tags":["php","easywechat","json","encoding","utf-8","encryption"],"backgroundTag":"json-encoding-failed","analyzedSha":"f0cf0a8b8361417ed683b8246d0ecbaf0aafcaa8","analyzedAt":"2026-08-21T05:29:19.565Z","schemaVersion":2},"datasetVersion":"2026-08-21T11:28:35.574Z"}