firefly-iii/firefly-iii · critical · FireflyException
The MAC is invalid.
Error message
The MAC is invalid.
What it means
Thrown by upgrade:480-decrypt-all when Laravel's Crypt::decrypt() fails with 'The MAC is invalid.' That message means the ciphertext's message authentication code does not match the current APP_KEY — i.e. the field was encrypted with a different key than the one in .env. The command re-throws it as FireflyException because silently passing wrong-key data through would corrupt the upgrade.
Source
Thrown at app/Console/Commands/Upgrade/RemovesDatabaseDecryption.php:180
return (bool) $configVar?->data;
}
/**
* Tries to decrypt data. Will only throw an exception when the MAC is invalid.
*
* @param mixed $value
*
* @return string
*
* @throws FireflyException
*/
private function tryDecrypt($value)
{
try {
$value = Crypt::decrypt($value);
} catch (DecryptException $e) {
if ('The MAC is invalid.' === $e->getMessage()) {
throw new FireflyException($e->getMessage(), 0, $e);
}
}
return $value;
}
}
View on GitHub (pinned to fd8791d08d)
Solutions
- Restore the ORIGINAL APP_KEY that encrypted the data in .env, clear config cache (php artisan config:clear), then re-run upgrade:480-decrypt-all.
- Search old .env files, container logs, or backups for the previous APP_KEY — it is the only way to decrypt the data.
- Once decryption succeeds and the upgrade completes, you may rotate APP_KEY again (future data uses the new key).
- If the old key is lost, the encrypted fields are unrecoverable; accept the loss (usually only 2FA secrets and similar fields) and re-set them in the UI.
Example fix
# before (.env after reinstall) APP_KEY=base64:NEWKEY... # upgrade:480-decrypt-all => The MAC is invalid. # after (restore the key that encrypted the DB) APP_KEY=base64:ORIGINALKEY... php artisan config:clear php artisan upgrade:480-decrypt-all
Defensive patterns
Strategy: try-catch
Validate before calling
// verify APP_KEY can decrypt a known encrypted value BEFORE mass upgrade
use Illuminate\Support\Facades\Crypt;
try {
$probe = \FireflyIII\Models\Preference::where('name', 'two_factor_secret')->first();
if (null !== $probe && null !== $probe->data_encrypted) {
Crypt::decryptString($probe->data_encrypted);
}
} catch (\Illuminate\Contracts\Encryption\DecryptException $e) {
// APP_KEY mismatch: restore the original key before running upgrade:480-decrypt-all
} Try / catch
use FireflyIII\Exceptions\FireflyException;
try {
$this->artisan('upgrade:480-decrypt-all');
} catch (FireflyException $e) {
if ('The MAC is invalid.' === $e->getMessage()) {
// stop: current APP_KEY != key that encrypted the data. Restore old APP_KEY, config:clear, re-run.
// do NOT continue the upgrade with mismatched data
}
} Prevention
- Pin APP_KEY in a secret manager; never let containers regenerate it.
- When restoring database backups, restore the matching .env/APP_KEY with them.
- Test decrypt-all on a copy before running it in production during upgrades.
When it happens
Trigger: Running php artisan firefly-iii:upgrade-database (or upgrade:480-decrypt-all) after changing APP_KEY or after restoring a database dump whose data was encrypted under an old key. Every encrypted column (e.g. two_factor_secret, older encrypted metadata) then fails MAC verification.
Common situations: Reinstalling Firefly III and generating a fresh APP_KEY while reusing the old database. Docker deployments without a pinned APP_KEY regenerating keys on rebuild. Copying .env.example into production (new key) with a restored backup. Rotating APP_KEY for security without planning re-encryption.
Related errors
- Webhooks are not enabled.
- Webhooks are not enabled.
- Webhooks are not enabled.
- Webhooks are not enabled.
- Webhooks are not enabled.
AI-assisted analysis of firefly-iii/firefly-iii@fd8791d08d (2026-08-17).
Data as JSON: /api/errors/667e98e0eee2113e.
Report an issue: GitHub.