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,
            $attributes

View on GitHub (pinned to f0cf0a8b83)

Solutions

  1. 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
  2. Verify aes_key is the exact 43-character EncodingAESKey shown in the WeChat/WeCom admin (decodes with the appended '=' to exactly 32 bytes)
  3. 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

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.