{"record":{"id":"b2c3aec652c5293a","repo":"plandex-ai/plandex","slug":"invalid-lock-scope-s","errorCode":null,"errorMessage":"invalid lock scope: %s","messagePattern":"invalid lock scope: (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"app/server/db/locks.go","lineNumber":102,"sourceCode":"\t}\n\n\torgId := params.OrgId\n\tuserId := params.UserId\n\tplanId := params.PlanId\n\tbranch := params.Branch\n\tscope := params.Scope\n\tplanBuildId := params.PlanBuildId\n\tctx := params.Ctx\n\tcancelFn := params.CancelFn\n\n\tif orgId == \"\" {\n\t\treturn \"\", fmt.Errorf(\"orgId is required\")\n\t}\n\tif planId == \"\" {\n\t\treturn \"\", fmt.Errorf(\"planId is required\")\n\t}\n\tif scope != LockScopeRead && scope != LockScopeWrite {\n\t\treturn \"\", fmt.Errorf(\"invalid lock scope: %s\", scope)\n\t}\n\n\ttx, err := Conn.BeginTxx(ctx, &sql.TxOptions{Isolation: sql.LevelRepeatableRead})\n\tif err != nil {\n\t\tif locksVerboseLogging {\n\t\t\tlog.Printf(\"[Lock][%d] Error starting transaction %v | reason: %s\",\n\t\t\t\tgoroutineID, err, params.Reason)\n\t\t}\n\t\treturn \"\", fmt.Errorf(\"error starting transaction: %v\", err)\n\t}\n\n\tvar committed bool\n\n\t// Ensure that rollback is attempted in case of failure\n\tdefer func() {\n\t\tif committed {\n\t\t\treturn\n\t\t}","sourceCodeStart":84,"sourceCodeEnd":120,"githubUrl":"https://github.com/plandex-ai/plandex/blob/e2d772072efadbe41d2946d97d79be55532dbab5/app/server/db/locks.go#L84-L120","documentation":"The Scope field of LockRepoParams must be exactly LockScopeRead or LockScopeWrite; any other LockScope value (empty string, typo, custom value) fails validation with \"invalid lock scope: %s\" before a transaction is opened.","triggerScenarios":"Passing a zero-value LockScope (empty string) in LockRepoParams, or a hand-written string that isn't one of the two defined constants.","commonSituations":"Constructing the params struct with named fields but omitting Scope; a config-driven scope string from env/flags that doesn't match the constants; renaming/refactoring a scope constant.","solutions":["Set Scope to db.LockScopeRead or db.LockScopeWrite constants — never raw strings.","Add a switch/default over the scope type so the compiler/CI flags unhandled values.","If scope comes from user input, normalize/validate it against the allowed set before building params."],"exampleFix":"// before\nparams := db.LockRepoParams{OrgId: orgId, PlanId: planId, Scope: db.LockScope(\"write_lock\")}\n// after\nscope := db.LockScopeWrite\nif readOnly { scope = db.LockScopeRead }\nparams := db.LockRepoParams{OrgId: orgId, PlanId: planId, Scope: scope}","handlingStrategy":"validation","validationCode":"func validScope(s db.LockScope) bool {\n    return s == db.LockScopeRead || s == db.LockScopeWrite\n}","typeGuard":"func isLockScope(s db.LockScope) bool {\n    switch s {\n    case db.LockScopeRead, db.LockScopeWrite:\n        return true\n    }\n    return false\n}","tryCatchPattern":"if !isLockScope(params.Scope) {\n    return fmt.Errorf(\"scope must be %s or %s, got %q\", db.LockScopeRead, db.LockScopeWrite, params.Scope)\n}\nlockId, err := db.LockRepo(ctx, cancel, params)","preventionTips":["Only reference the exported LockScopeRead/LockScopeWrite constants; never cast raw strings to LockScope.","Always set Scope in struct literals — don't rely on zero values.","Add a linter/test enumerating all valid LockScope values to catch fork-added constants."],"tags":["validation","locking","input-validation"],"backgroundTag":"invalid-enum-value","analyzedSha":"e2d772072efadbe41d2946d97d79be55532dbab5","analyzedAt":"2026-09-05T20:56:53.631Z","contentChangedAt":"2026-09-05T20:56:53.631Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}