octobercms/october · warning · ApplicationException
Cannot delete this template because it is locked
Error message
Cannot delete this template because it is locked
What it means
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.
Source
Thrown at modules/system/models/MailLayout.php:62
/**
* @var array jsonable attribute names that are json encoded and decoded from the database
*/
protected $jsonable = [
'options'
];
/**
* @var array codeCache
*/
public static $codeCache;
/**
* beforeDelete
*/
public function beforeDelete()
{
if ($this->is_locked) {
throw new ApplicationException('Cannot delete this template because it is locked');
}
}
/**
* listCodes
*/
public static function listCodes()
{
if (self::$codeCache !== null) {
return self::$codeCache;
}
return self::$codeCache = self::lists('id', 'code');
}
/**
* getIdFromCode
*/View on GitHub (pinned to b608633a7e)
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
Example fix
// before $layout->delete(); // throws: is_locked // after — deliberate removal of a system layout $layout->is_locked = false; $layout->save(); $layout->delete();
Defensive patterns
Strategy: type-guard
Validate before calling
if ($layout->is_locked) {
// refuse deletion in the UI, or explicitly unlock first if removal is intended
} Try / catch
try { $layout->delete(); } catch (\ApplicationException $ex) { /* keep the row and inform the user it is system-managed */ } Prevention
- 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
When it happens
Trigger: $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.
Common situations: Trying to remove the default mail layout during cleanup; import/sync scripts deleting every mail_layouts row.
Related errors
- Unable to find a registered layout with code:
- cms::lang.layout.not_found_name
- cms::lang.lang.error_deleting_file
- Cannot delete the active theme, try making another theme act
- Unable to find a registered partial with code:
AI-assisted analysis of octobercms/october@b608633a7e (2026-08-21).
Data as JSON: /api/errors/868f4b587c9f4148.
Report an issue: GitHub.