w7corp/easywechat · error · RuntimeException
-40012
-40012
Error message
Invalid json data.
What it means
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.
Source
Thrown at src/Kernel/Encryptor.php:129
return Xml::build($response);
}
public function encryptAsJson(string $plaintext, ?string $nonce = null, int|string|null $timestamp = null): string
{
$encrypted = $this->encryptAsArray($plaintext, $nonce, $timestamp);
$response = [
'encrypt' => $encrypted['ciphertext'],
'msgsignature' => $encrypted['signature'],
'timestamp' => $encrypted['timestamp'],
'nonce' => $encrypted['nonce'],
];
$jsonStr = json_encode($response, JSON_UNESCAPED_UNICODE);
if ($jsonStr === false) {
throw new RuntimeException('Invalid json data.', self::ERROR_JSON_BUILD);
}
return $jsonStr;
}
/**
* @throws RuntimeException
*/
public function encryptAsArray(string $plaintext, ?string $nonce = null, int|string|null $timestamp = null): array
{
try {
$plaintext = Pkcs7::padding(
random_bytes(self::BLOCK_SIZE).pack('N', strlen($plaintext)).$plaintext.$this->appId,
blockSize: strlen($this->aesKey)
);
$ciphertext = base64_encode(
openssl_encrypt(
$plaintext,View on GitHub (pinned to f0cf0a8b83)
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
Example fix
// before $reply = $encryptor->encrypt($messageWithBrokenUtf8, null, null, 'json'); // after $clean = mb_convert_encoding($messageWithBrokenUtf8, 'UTF-8', 'UTF-8'); $reply = $encryptor->encrypt($clean, null, null, 'json');
Defensive patterns
Strategy: validation
Validate before calling
// Reject invalid UTF-8 before encrypting JSON replies
$clean = mb_convert_encoding($plaintext, 'UTF-8', 'UTF-8');
if ($clean === null || !mb_check_encoding($clean, 'UTF-8')) {
throw new InvalidArgumentException('Plaintext must be valid UTF-8');
}
$reply = $encryptor->encrypt($clean, null, null, 'json'); Try / catch
try {
$json = $encryptor->encryptAsJson($plaintext);
} catch (\EasyWeChat\Kernel\Exceptions\RuntimeException $e) {
if ($e->getCode() === \EasyWeChat\Kernel\Encryptor::ERROR_JSON_BUILD) {
// sanitize and retry once
$json = $encryptor->encryptAsJson(mb_convert_encoding($plaintext, 'UTF-8', 'UTF-8'));
} else {
throw $e;
}
} Prevention
- 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
When it happens
Trigger: 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).
Common situations: 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.
Understand the failure class
- Parsing and encoding errors: unexpected token, malformed input — why parsers reject input and how to find the real culprit.
Related errors
AI-assisted analysis of w7corp/easywechat@f0cf0a8b83 (2026-08-21).
Data as JSON: /api/errors/608a09f25656eb61.
Report an issue: GitHub.