{"record":{"id":"667e98e0eee2113e","repo":"firefly-iii/firefly-iii","slug":"the-mac-is-invalid","errorCode":null,"errorMessage":"The MAC is invalid.","messagePattern":"The MAC is invalid\\.","errorType":"console","errorClass":"FireflyException","httpStatus":null,"severity":"critical","filePath":"app/Console/Commands/Upgrade/RemovesDatabaseDecryption.php","lineNumber":180,"sourceCode":"        return (bool) $configVar?->data;\n    }\n\n    /**\n     * Tries to decrypt data. Will only throw an exception when the MAC is invalid.\n     *\n     * @param mixed $value\n     *\n     * @return string\n     *\n     * @throws FireflyException\n     */\n    private function tryDecrypt($value)\n    {\n        try {\n            $value = Crypt::decrypt($value);\n        } catch (DecryptException $e) {\n            if ('The MAC is invalid.' === $e->getMessage()) {\n                throw new FireflyException($e->getMessage(), 0, $e);\n            }\n        }\n\n        return $value;\n    }\n}\n","sourceCodeStart":162,"sourceCodeEnd":187,"githubUrl":"https://github.com/firefly-iii/firefly-iii/blob/fd8791d08d4d9e6467519a78048cd038e26b8878/app/Console/Commands/Upgrade/RemovesDatabaseDecryption.php#L162-L187","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"# before (.env after reinstall)\nAPP_KEY=base64:NEWKEY...\n# upgrade:480-decrypt-all => The MAC is invalid.\n\n# after (restore the key that encrypted the DB)\nAPP_KEY=base64:ORIGINALKEY...\nphp artisan config:clear\nphp artisan upgrade:480-decrypt-all","handlingStrategy":"try-catch","validationCode":"// verify APP_KEY can decrypt a known encrypted value BEFORE mass upgrade\nuse Illuminate\\Support\\Facades\\Crypt;\ntry {\n    $probe = \\FireflyIII\\Models\\Preference::where('name', 'two_factor_secret')->first();\n    if (null !== $probe && null !== $probe->data_encrypted) {\n        Crypt::decryptString($probe->data_encrypted);\n    }\n} catch (\\Illuminate\\Contracts\\Encryption\\DecryptException $e) {\n    // APP_KEY mismatch: restore the original key before running upgrade:480-decrypt-all\n}","typeGuard":null,"tryCatchPattern":"use FireflyIII\\Exceptions\\FireflyException;\n\ntry {\n    $this->artisan('upgrade:480-decrypt-all');\n} catch (FireflyException $e) {\n    if ('The MAC is invalid.' === $e->getMessage()) {\n        // stop: current APP_KEY != key that encrypted the data. Restore old APP_KEY, config:clear, re-run.\n        // do NOT continue the upgrade with mismatched data\n    }\n}","preventionTips":["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."],"tags":["console","upgrade-command","encryption","app-key","configuration"],"backgroundTag":"app-key-mismatch","analyzedSha":"fd8791d08d4d9e6467519a78048cd038e26b8878","analyzedAt":"2026-08-17T02:14:53.848Z","schemaVersion":2},"datasetVersion":"2026-08-17T04:17:16.089Z"}