{"record":{"id":"f096256f19453d37","repo":"mattermost-community/focalboard","slug":"cannot-leave-a-board-with-no-admins","errorCode":null,"errorMessage":"cannot leave a board with no admins","messagePattern":"cannot leave a board with no admins","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"error","filePath":"server/model/error.go","lineNumber":24,"sourceCode":"\t\"fmt\"\n\t\"net/http\"\n\t\"strings\"\n\n\tmmModel \"github.com/mattermost/mattermost/server/public/model\"\n\n\tpluginapi \"github.com/mattermost/mattermost/server/public/pluginapi\"\n)\n\nvar (\n\tErrViewsLimitReached        = errors.New(\"views limit reached for board\")\n\tErrPatchUpdatesLimitedCards = errors.New(\"patch updates cards that are limited\")\n\n\tErrInsufficientLicense = errors.New(\"appropriate license required\")\n\n\tErrCategoryPermissionDenied = errors.New(\"category doesn't belong to user\")\n\tErrCategoryDeleted          = errors.New(\"category is deleted\")\n\n\tErrBoardMemberIsLastAdmin = errors.New(\"cannot leave a board with no admins\")\n\n\tErrRequestEntityTooLarge = errors.New(\"request entity too large\")\n\n\tErrInvalidBoardSearchField = errors.New(\"invalid board search field\")\n)\n\n// ErrNotFound is an error type that can be returned by store APIs\n// when a query unexpectedly fetches no records.\ntype ErrNotFound struct {\n\tentity string\n}\n\n// NewErrNotFound creates a new ErrNotFound instance.\nfunc NewErrNotFound(entity string) *ErrNotFound {\n\treturn &ErrNotFound{\n\t\tentity: entity,\n\t}\n}","sourceCodeStart":6,"sourceCodeEnd":42,"githubUrl":"https://github.com/mattermost-community/focalboard/blob/a84bbb65e32edf972856b329417096ac413518e9/server/model/error.go#L6-L42","documentation":"ErrBoardMemberIsLastAdmin is returned when a board membership operation would remove the last admin of a board. Boards must retain at least one admin member, so UpdateBoardMember (demoting to a lower role) and DeleteBoardMember (leaving/removal) check this and reject the change with this sentinel error. IsErrBadRequest maps it to HTTP 400.","triggerScenarios":"The last board admin calls leave board; DeleteBoardMember removes the only member with the Admin role; UpdateBoardMember changes the sole Admin's role to Editor/Viewer.","commonSituations":"Sole board owner leaving a team and trying to hand off incorrectly; bulk offboarding scripts that remove the last admin; role-change UI on the only admin row.","solutions":["Promote another member to Admin first, then leave/demote the original admin","Have a system admin reassign board ownership via admin APIs","Use the UI's ownership transfer flow which handles the handoff"],"exampleFix":"// before\nerr := app.DeleteBoardMember(boardID, lastAdminUserID)\n// after\nerr := app.UpdateBoardMember(boardID, newAdminUserID, model.BoardRoleAdmin)\nif err != nil { return err }\nerr = app.DeleteBoardMember(boardID, lastAdminUserID)","handlingStrategy":"type-guard","validationCode":"members, err := app.GetMembersForBoard(boardID)\nif err == nil && countAdmins(members) <= 1 && memberIsLastAdmin {\n    // block the leave/demote and prompt ownership transfer\n}","typeGuard":"func isLastAdminErr(err error) bool {\n    return errors.Is(err, model.ErrBoardMemberIsLastAdmin)\n}","tryCatchPattern":"err := app.DeleteBoardMember(boardID, userID)\nif errors.Is(err, model.ErrBoardMemberIsLastAdmin) {\n    // ask user to promote another member first\n}","preventionTips":["Promote a successor admin before leaving/removing admins","Check admin count in offboarding scripts","Expose ownership-transfer UI to the sole admin"],"tags":["go","permissions","board-members","last-admin"],"backgroundTag":"last-admin-removal-blocked","analyzedSha":"a84bbb65e32edf972856b329417096ac413518e9","analyzedAt":"2026-08-30T09:22:20.720Z","schemaVersion":2},"datasetVersion":"2026-08-30T13:17:10.514Z"}