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

  1. Set Scope to db.LockScopeRead or db.LockScopeWrite constants — never raw strings.
  2. Add a switch/default over the scope type so the compiler/CI flags unhandled values.
  3. 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

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


AI-assisted analysis of plandex-ai/plandex@e2d772072e (2026-09-05). Data as JSON: /api/errors/b2c3aec652c5293a. Report an issue: GitHub.