{"record":{"id":"3aec5ef125ba0e60","repo":"Tencent/WeKnora","slug":"repository-last-active-owner","errorCode":null,"errorMessage":"repository: last active owner","messagePattern":"repository: last active owner","errorType":"error_code","errorClass":null,"httpStatus":400,"severity":"error","filePath":"internal/application/repository/tenant_member.go","lineNumber":19,"sourceCode":"package repository\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/Tencent/WeKnora/internal/types\"\n\t\"github.com/Tencent/WeKnora/internal/types/interfaces\"\n\t\"gorm.io/gorm\"\n\t\"gorm.io/gorm/clause\"\n)\n\n// ErrLastOwner is returned by the atomic demote / remove repo helpers\n// when the operation would leave the tenant without an active Owner.\n// The service layer maps this to its own ErrLastOwner sentinel (same\n// semantic; just kept separate so the repo doesn't import service).\nvar ErrLastOwner = errors.New(\"repository: last active owner\")\n\n// forUpdateClause returns the gorm SELECT ... FOR UPDATE clause. Kept\n// in one place so we can swap it out for `clause.Locking{Strength: \"UPDATE\"}`\n// on databases that don't support row-level locking (none in our matrix,\n// but keeps the seam if SQLite-lite ever needs a no-op).\nfunc forUpdateClause() clause.Expression {\n\treturn clause.Locking{Strength: \"UPDATE\"}\n}\n\n// tenantMemberRepository implements interfaces.TenantMemberRepository.\ntype tenantMemberRepository struct {\n\tdb *gorm.DB\n}\n\n// NewTenantMemberRepository creates a new tenant member repository.\nfunc NewTenantMemberRepository(db *gorm.DB) interfaces.TenantMemberRepository {\n\treturn &tenantMemberRepository{db: db}\n}","sourceCodeStart":1,"sourceCodeEnd":37,"githubUrl":"https://github.com/Tencent/WeKnora/blob/988cbb03305e055d8ebb7d46d9ac6cc0803cd074/internal/application/repository/tenant_member.go#L1-L37","documentation":"Sentinel ErrLastOwner returned by atomic demote/remove member helpers after row-locking active owners (FOR UPDATE): if the locked owner set would drop to zero — i.e. the target is the last active Owner — the operation aborts to keep the tenant with at least one owner.","triggerScenarios":"Thrown at internal/application/repository/tenant_member.go:19 when the library encounters an invalid state.","commonSituations":"See trigger scenarios.","solutions":["Promote another member to Owner before demoting/removing the current last owner","Reject the operation with 400/409 in the service layer and explain the last-owner constraint"],"exampleFix":null,"handlingStrategy":"type-guard","validationCode":null,"typeGuard":null,"tryCatchPattern":null,"preventionTips":[],"tags":[],"backgroundTag":null,"analyzedSha":"988cbb03305e055d8ebb7d46d9ac6cc0803cd074","analyzedAt":"2026-09-02T14:41:08.344Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}