w7corp/easywechat · error · RuntimeException
-40006
-40006
Error message
$e->getMessage()
What it means
Encryptor::encryptAsArray() wraps the PKCS7 padding + AES-256-CBC encryption block in a try/catch and rethrows any Throwable as RuntimeException(code -40006 ERROR_ENCRYPT_AES). The constructor decodes the key via base64_decode($aesKey.'='), so a wrong-length EncodingAESKey produces an aesKey whose strlen is not 32 — Pkcs7::padding then throws ('$blockSize may not be more than 32 bytes' when strlen > 32) or PHP throws DivisionByZeroError ('Modulo by zero') when the key decoded to an empty string.
Source
Thrown at src/Kernel/Encryptor.php:155
*/
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,
'aes-256-cbc',
$this->aesKey,
OPENSSL_NO_PADDING,
iv: substr($this->aesKey, 0, self::BLOCK_SIZE)
) ?: ''
);
} catch (Throwable $e) {
throw new RuntimeException($e->getMessage(), self::ERROR_ENCRYPT_AES);
}
$nonce ??= Str::random();
$timestamp ??= time();
return [
'ciphertext' => $ciphertext,
'signature' => $this->createSignature($this->token, $timestamp, $nonce, $ciphertext),
'timestamp' => $timestamp,
'nonce' => $nonce,
];
}
public function createSignature(string|int ...$attributes): string
{
$attributes = array_map(
static fn (string|int $attribute): string => (string) $attribute,
$attributesView on GitHub (pinned to f0cf0a8b83)
Solutions
- Inspect the exception message: 'Modulo by zero' means aes_key decoded to an empty string; '$blockSize may not be more than 32 bytes' means it decoded to >32 bytes — both point at a malformed EncodingAESKey
- Verify aes_key is the exact 43-character EncodingAESKey shown in the WeChat/WeCom admin (decodes with the appended '=' to exactly 32 bytes)
- Double-check you did not include quotes, spaces or newlines when copying the key into config/env
Example fix
// before
$encryptor = new Encryptor($appId, $token, 'wrong-or-empty-key');
$encryptor->encrypt('hello'); // RuntimeException -40006
// after: use the exact 43-char EncodingAESKey
$encodingAesKey = 'abcdefghijklmnopqrstuvwxyz0123456789ABCDEFG'; // 43 chars
$encryptor = new Encryptor($appId, $token, $encodingAesKey);
$encryptor->encrypt('hello'); Defensive patterns
Strategy: validation
Validate before calling
// Validate the EncodingAESKey before constructing the Encryptor
function isValidEncodingAesKey(string $key): bool
{
return strlen($key) === 43 && strlen(base64_decode($key.'=', true)) === 32;
}
if (! isValidEncodingAesKey($config['aes_key'])) {
throw new InvalidArgumentException('aes_key must be the 43-char EncodingAESKey');
} Try / catch
try {
$encrypted = $encryptor->encryptAsArray($plaintext);
} catch (\EasyWeChat\Kernel\Exceptions\RuntimeException $e) {
if ($e->getCode() === \EasyWeChat\Kernel\Encryptor::ERROR_ENCRYPT_AES) {
// message is 'Modulo by zero' (empty decoded key) or '$blockSize may not be more than 32 bytes' -> wrong aes_key
report_config_error('Invalid EncodingAESKey', $e->getMessage());
}
throw $e;
} Prevention
- Add a boot-time assertion that base64_decode(aes_key.'=') is exactly 32 bytes
- Copy keys from the admin console with trailing-whitespace trimming (trim() on load)
When it happens
Trigger: Configuring Encryptor/app with an aes_key that is not the 43-char base64 EncodingAESKey: an empty string, a wrong-length string, an already-base64-decoded binary key, or one with trailing whitespace/typo — so strlen($this->aesKey) != 32 and padding fails before openssl_encrypt runs.
Common situations: Copy-paste errors in EncodingAESKey from the WeChat admin console, confusing the plain token with aes_key, reusing an OfficialAccount key for a Work app (or vice versa), passing the 44-char padded base64 instead of the 43-char key.
Related errors
AI-assisted analysis of w7corp/easywechat@f0cf0a8b83 (2026-08-21).
Data as JSON: /api/errors/2972f28aed48c035.
Report an issue: GitHub.