plandex-ai/plandex · error
invalid lock scope: %s
Error message
invalid lock scope: %s
What it means
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.
Source
Thrown at app/server/db/locks.go:102
}
orgId := params.OrgId
userId := params.UserId
planId := params.PlanId
branch := params.Branch
scope := params.Scope
planBuildId := params.PlanBuildId
ctx := params.Ctx
cancelFn := params.CancelFn
if orgId == "" {
return "", fmt.Errorf("orgId is required")
}
if planId == "" {
return "", fmt.Errorf("planId is required")
}
if scope != LockScopeRead && scope != LockScopeWrite {
return "", fmt.Errorf("invalid lock scope: %s", scope)
}
tx, err := Conn.BeginTxx(ctx, &sql.TxOptions{Isolation: sql.LevelRepeatableRead})
if err != nil {
if locksVerboseLogging {
log.Printf("[Lock][%d] Error starting transaction %v | reason: %s",
goroutineID, err, params.Reason)
}
return "", fmt.Errorf("error starting transaction: %v", err)
}
var committed bool
// Ensure that rollback is attempted in case of failure
defer func() {
if committed {
return
}View on GitHub (pinned to e2d772072e)
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.
Example fix
// before
params := db.LockRepoParams{OrgId: orgId, PlanId: planId, Scope: db.LockScope("write_lock")}
// after
scope := db.LockScopeWrite
if readOnly { scope = db.LockScopeRead }
params := db.LockRepoParams{OrgId: orgId, PlanId: planId, Scope: scope} Defensive patterns
Strategy: validation
Validate before calling
func validScope(s db.LockScope) bool {
return s == db.LockScopeRead || s == db.LockScopeWrite
} Type guard
func isLockScope(s db.LockScope) bool {
switch s {
case db.LockScopeRead, db.LockScopeWrite:
return true
}
return false
} Try / catch
if !isLockScope(params.Scope) {
return fmt.Errorf("scope must be %s or %s, got %q", db.LockScopeRead, db.LockScopeWrite, params.Scope)
}
lockId, err := db.LockRepo(ctx, cancel, params) Prevention
- 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.
When it happens
Trigger: Passing a zero-value LockScope (empty string) in LockRepoParams, or a hand-written string that isn't one of the two defined constants.
Common situations: 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.
Understand the failure class
Background: Invalid enum value errors: "Unknown type", "Invalid scope", "must be one of" — when a string is not on the library's allowed list — this error's family across 23 libraries.
Related errors
- orgId is required
- planId is required
- invalid lock scope: %v
- error prompting host: %v
- error prompting email: %v
AI-assisted analysis of plandex-ai/plandex@e2d772072e (2026-09-05).
Data as JSON: /api/errors/b2c3aec652c5293a.
Report an issue: GitHub.