phalcon/cphalcon · error · EmptyDecryptionKey
Decryption key cannot be empty
Error message
Decryption key cannot be empty
What it means
Crypt::decrypt() requires a key: it uses the key passed as the second argument, falling back to the key set earlier with setKey(). If both are absent or empty, it throws EmptyDecryptionKey before touching any ciphertext. This is a configuration error, not a data error.
Source
Thrown at phalcon/Encryption/Crypt.zep:230
* @param string|null $key
*
* @return string
* @throws Exception
* @throws InvalidDecryptLength
* @throws Mismatch
*/
public function decrypt(string input, string key = null) -> string
{
var blockSize, cipher, cipherText, decrypted, decryptKey, digest,
hashAlgorithm, hashLength, iv, ivLength, mode;
let decryptKey = this->key;
if true !== empty(key) {
let decryptKey = key;
}
if true === empty(decryptKey) {
throw new EmptyDecryptionKey();
}
let cipher = this->cipher,
ivLength = this->ivLength;
this->checkCipherHashIsAvailable(cipher, "cipher");
if true !== this->isValidDecryptLength(input) {
throw new InvalidDecryptLength();
}
let mode = this->getMode(),
blockSize = this->getBlockSize(mode),
iv = mb_substr(input, 0, ivLength, "8bit");
/**
* Check if we have chosen signing and use the hash
*/View on GitHub (pinned to b7419de9cd)
Solutions
- Call $crypt->setKey($key) once at setup (DI definition) with a cryptographically random key of the exact length your cipher needs (e.g. 32 bytes for aes-256).
- Or pass the key per call: $crypt->decrypt($payload, $key).
- Load the key from a single env/config source and fail application boot if it is empty, so misconfiguration surfaces at startup rather than mid-request.
- Ensure the SAME key is used for encrypt and decrypt (see Mismatch otherwise).
Example fix
// before
$crypt = new \Phalcon\Encryption\Crypt();
$plain = $crypt->decrypt($token); // no key ever set
// after
$crypt = new \Phalcon\Encryption\Crypt();
$crypt->setKey(getenv('APP_ENCRYPTION_KEY')); // validated non-empty at boot
$plain = $crypt->decrypt($token); Defensive patterns
Strategy: validation
Validate before calling
$key = $config->path('encryption.key');
if (empty($key)) {
throw new \RuntimeException('Encryption key missing - check encryption.key config');
}
$crypt->setKey($key);
// only now call decrypt() Type guard
function hasCryptKey(\Phalcon\Encryption\Crypt $crypt): bool
{
return '' !== $crypt->getKey();
} Try / catch
try {
$plain = $crypt->decrypt($cipherText);
} catch (\Phalcon\Encryption\Crypt\Exception\EmptyDecryptionKey $e) {
// configuration bug - do not retry; fail fast with context
throw new \RuntimeException('Crypt used without a key - check DI setup', 0, $e);
} Prevention
- Configure the key once in the DI definition and assert non-empty at boot.
- Fail application startup when the key env var is missing rather than letting first decrypt blow up.
- Keep a health check that verifies getKey() is non-empty in production.
When it happens
Trigger: Creating $crypt = new Crypt() and calling decrypt($ciphertext) without ever calling setKey(), or calling setKey('') / setKey(null-ish) and decrypt() without the second argument. Also when a shared Crypt instance is built from config and the config key is missing/empty in one environment.
Common situations: Environment-specific config where the encryption key env var (e.g. APP_KEY) is defined in production but not locally or in CI; instantiating Crypt inline in a helper instead of pulling the configured instance from the DI container; trimming/rotating keys and accidentally storing an empty string.
Related errors
- Encryption key cannot be empty
- Hash does not match.
- The auth tag length must be between 4 and 16 bytes.
- Auth data must be provided when using AEAD mode
- Could not encrypt data
AI-assisted analysis of phalcon/cphalcon@b7419de9cd (2026-08-21).
Data as JSON: /api/errors/60508a3bc058f7e6.
Report an issue: GitHub.