phalcon/cphalcon · error · EmptyEncryptionKey
Encryption key cannot be empty
Error message
Encryption key cannot be empty
What it means
Crypt::encrypt() needs a key: the second argument if given, otherwise the one set via setKey(). If both are empty it throws EmptyEncryptionKey before generating an IV. Like its decrypt twin, this is a hard configuration failure - Phalcon refuses to encrypt with no key rather than silently using empty material.
Source
Thrown at phalcon/Encryption/Crypt.zep:337
*
* @param string $input
* @param string|null $key
*
* @return string
* @throws Exception
*/
public function encrypt(string input, string key = null) -> string
{
var blockSize, cipher, digest, encryptKey, encrypted, iv, ivLength,
mode, padded;
let encryptKey = this->key;
if true !== empty(key) {
let encryptKey = key;
}
if true === empty(encryptKey) {
throw new EmptyEncryptionKey();
}
let cipher = this->cipher,
ivLength = this->ivLength;
this->checkCipherHashIsAvailable(cipher, "cipher");
let mode = this->getMode(),
blockSize = this->getBlockSize(mode);
try {
let iv = this->phpOpensslRandomPseudoBytes(ivLength);
} catch \Throwable {
throw new RandomBytesGenerationFailed();
}
let padded = this->encryptGetPadded(mode, input, blockSize);
View on GitHub (pinned to b7419de9cd)
Solutions
- Set the key at construction time in your DI setup: $crypt->setKey($config->encryption->key) after asserting it is non-empty.
- Or pass it per call: $crypt->encrypt($data, $key).
- Add a boot-time assertion: if (empty($_ENV['APP_ENCRYPTION_KEY'])) { throw new RuntimeException('Encryption key missing'); } so the app fails fast.
- Generate the key with bin2hex(random_bytes(32)) once and store it in a secret manager/env - never derive it from a password.
Example fix
// before
$di->set('crypt', function () {
$crypt = new \Phalcon\Encryption\Crypt();
return $crypt; // key never configured
});
$stored = $di->get('crypt')->encrypt($secret); // throws
// after
$di->set('crypt', function () use ($config) {
$key = $config->path('encryption.key');
if (empty($key)) {
throw new \RuntimeException('encryption.key is not configured');
}
return (new \Phalcon\Encryption\Crypt())->setKey($key);
}); Defensive patterns
Strategy: validation
Validate before calling
$key = $_ENV['APP_ENCRYPTION_KEY'] ?? '';
if ('' === $key) {
throw new \RuntimeException('APP_ENCRYPTION_KEY is not set');
}
$crypt->setKey($key);
$cipherText = $crypt->encrypt($data); Type guard
function hasCryptKey(\Phalcon\Encryption\Crypt $crypt): bool
{
return '' !== $crypt->getKey();
} Try / catch
try {
$stored = $crypt->encrypt($data);
} catch (\Phalcon\Encryption\Crypt\Exception\EmptyEncryptionKey $e) {
throw new \RuntimeException('Cannot encrypt: no key configured', 0, $e);
} Prevention
- Derive the exact-length key once at boot and inject the configured Crypt from DI everywhere.
- Add a startup check that both reads and encrypts a probe string - it exercises the full path.
- Never silently skip encryption when the key is missing; storing plaintext as a fallback defeats the control.
When it happens
Trigger: new Crypt() followed directly by encrypt($data); setKey('') then encrypt($data) without the second argument; a DI-shared Crypt whose key is injected from an empty config/env value in this environment.
Common situations: Missing APP_KEY-style env var in CI, Docker, or a new developer's .env; config cached before the key existed; key value loaded with a wrong config path so it resolves to null and casts to empty string.
Related errors
- Decryption 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/babaad77e79815b9.
Report an issue: GitHub.