{"record":{"id":"868f4b587c9f4148","repo":"octobercms/october","slug":"cannot-delete-this-template-because-it-is-locked","errorCode":null,"errorMessage":"Cannot delete this template because it is locked","messagePattern":"Cannot delete this template because it is locked","errorType":"exception","errorClass":"ApplicationException","httpStatus":null,"severity":"warning","filePath":"modules/system/models/MailLayout.php","lineNumber":62,"sourceCode":"    /**\n     * @var array jsonable attribute names that are json encoded and decoded from the database\n     */\n    protected $jsonable = [\n        'options'\n    ];\n\n    /**\n     * @var array codeCache\n     */\n    public static $codeCache;\n\n    /**\n     * beforeDelete\n     */\n    public function beforeDelete()\n    {\n        if ($this->is_locked) {\n            throw new ApplicationException('Cannot delete this template because it is locked');\n        }\n    }\n\n    /**\n     * listCodes\n     */\n    public static function listCodes()\n    {\n        if (self::$codeCache !== null) {\n            return self::$codeCache;\n        }\n\n        return self::$codeCache = self::lists('id', 'code');\n    }\n\n    /**\n     * getIdFromCode\n     */","sourceCodeStart":44,"sourceCodeEnd":80,"githubUrl":"https://github.com/octobercms/october/blob/b608633a7e8922487d91a8161499020121c3b3bf/modules/system/models/MailLayout.php#L44-L80","documentation":"MailLayout::beforeDelete() blocks deletion of locked mail layouts. Layouts seeded from registration carry is_locked = 1 (e.g. the built-in 'default' layout that system and plugin emails extend), and deleting such a row throws 'Cannot delete this template because it is locked' before the delete query runs.","triggerScenarios":"$layout->delete() on a row with is_locked true; the backend mail layout delete action on a locked layout; bulk deletion iterating all layouts without filtering is_locked.","commonSituations":"Trying to remove the default mail layout during cleanup; import/sync scripts deleting every mail_layouts row.","solutions":["Delete a non-locked (custom) layout instead — locked ones are system-managed","If you truly must remove it, unlock first: set is_locked to false, save, then delete","In bulk scripts, skip locked rows: iterate MailLayout::where('is_locked', false)->get() and delete each"],"exampleFix":"// before\n$layout->delete(); // throws: is_locked\n\n// after — deliberate removal of a system layout\n$layout->is_locked = false;\n$layout->save();\n$layout->delete();","handlingStrategy":"type-guard","validationCode":"if ($layout->is_locked) {\n    // refuse deletion in the UI, or explicitly unlock first if removal is intended\n}","typeGuard":null,"tryCatchPattern":"try { $layout->delete(); } catch (\\ApplicationException $ex) { /* keep the row and inform the user it is system-managed */ }","preventionTips":["Filter bulk deletions with where('is_locked', false)","Treat locked layouts as system-managed; extend rather than delete them","Show the lock state in the mail layout UI so users understand why delete fails"],"tags":["mail","layout","delete","lock"],"backgroundTag":"delete-locked-resource","analyzedSha":"b608633a7e8922487d91a8161499020121c3b3bf","analyzedAt":"2026-08-21T04:24:57.515Z","schemaVersion":2},"datasetVersion":"2026-08-21T11:28:35.574Z"}