{"record":{"id":"8118a332cf0c4fd9","repo":"plandex-ai/plandex","slug":"invalid-lock-scope-v","errorCode":null,"errorMessage":"invalid lock scope: %v","messagePattern":"invalid lock scope: (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"app/server/db/locks.go","lineNumber":275,"sourceCode":"\t\tif scope == LockScopeRead {\n\t\t\t// if we're trying to acquire a read lock, we can do so unless there's a conflicting lock\n\t\t\t// a write lock always conflicts with a read lock (regardless of branch)\n\t\t\t// a read lock conflicts if it's for a different branch (since it would need to checkout a different branch in the middle of an already-running read)\n\t\t\tif lock.Scope == LockScopeWrite {\n\t\t\t\tcanAcquire = false\n\t\t\t\tbreak\n\t\t\t} else if lock.Scope == LockScopeRead {\n\t\t\t\tif lockBranch != branch {\n\t\t\t\t\tcanAcquire = false\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t}\n\t\t} else if scope == LockScopeWrite {\n\t\t\t// if we're trying to acquire a write lock, we can only do so if there's no other lock (read or write)\n\t\t\tcanAcquire = false\n\t\t\tbreak\n\t\t} else {\n\t\t\terr = fmt.Errorf(\"invalid lock scope: %v\", scope)\n\t\t\treturn \"\", err\n\t\t}\n\t}\n\n\tif !canAcquire {\n\t\tif locksVerboseLogging {\n\t\t\tlog.Println(\"can't acquire lock.\", \"numRetry:\", numRetry)\n\t\t}\n\t\tconflictErr := errors.New(\"lock conflict: cannot acquire read/write lock\")\n\t\tlog.Printf(\"[Lock][%d] can't acquire lock, retrying: %v | reason: %s | now: %s | locks:\\n%s\\n\", goroutineID, conflictErr, params.Reason, now, spew.Sdump(locks))\n\n\t\treturn retryWithExponentialBackoff(params.Ctx, conflictErr, numRetry, func(nextAttempt int) (string, error) {\n\t\t\treturn lockRepoDB(params, nextAttempt)\n\t\t})\n\t}\n\n\tif locksVerboseLogging {\n\t\tlog.Println(\"can acquire lock - inserting new lock\")","sourceCodeStart":257,"sourceCodeEnd":293,"githubUrl":"https://github.com/plandex-ai/plandex/blob/e2d772072efadbe41d2946d97d79be55532dbab5/app/server/db/locks.go#L257-L293","documentation":"After fetching existing locks, lockRepoDB evaluates acquisition per lock; the read/write logic only handles LockScopeRead and LockScopeWrite. If scope somehow holds any other value at this point (it should have been caught by earlier validation), the loop returns \"invalid lock scope: %v\", which propagates to lock acquisition retry logic.","triggerScenarios":"A LockScope value that passes none of the read/write equality checks in the acquisition loop — practically a zero-value/empty scope or a constant mismatch introduced after the initial validation.","commonSituations":"Custom LockScope constants added by a fork without extending the acquisition branch; scope mutated between validation and the loop; a default-constructed LockRepoParams whose Scope validation was bypassed by calling lockRepoDB directly.","solutions":["Always pass db.LockScopeRead or db.LockScopeWrite; never construct custom LockScope values.","Call the exported lock API rather than lockRepoDB directly so upfront validation runs.","Extend the acquisition branch if you genuinely added a new scope constant.","Add a default case/test asserting LockScope only has the two valid values."],"exampleFix":"// before\nscope := db.LockScope(req.Scope) // arbitrary user string\n// after\nvar scope db.LockScope\nswitch req.Scope {\ncase \"read\": scope = db.LockScopeRead\ncase \"write\": scope = db.LockScopeWrite\ndefault: return fmt.Errorf(\"unsupported scope %q\", req.Scope)\n}","handlingStrategy":"validation","validationCode":"if params.Scope != db.LockScopeRead && params.Scope != db.LockScopeWrite {\n    return fmt.Errorf(\"scope must be LockScopeRead or LockScopeWrite\")\n}","typeGuard":"func isKnownLockScope(s db.LockScope) bool {\n    return s == db.LockScopeRead || s == db.LockScopeWrite\n}","tryCatchPattern":"lockId, err := db.LockRepo(ctx, cancel, params)\nif err != nil && strings.Contains(err.Error(), \"invalid lock scope\") {\n    return fmt.Errorf(\"programming error: scope %q is not supported\", params.Scope) // do not retry\n}","preventionTips":["Never invent new LockScope values; restrict the type to the two exported constants.","Call the exported lock API so upfront scope validation runs before the acquisition loop.","Add an exhaustive-switch test over LockScope to catch any newly added constant early."],"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-12T22:17:10.623Z"}