{"record":{"id":"ed62d569bc53ce7f","repo":"plandex-ai/plandex","slug":"orgid-is-required","errorCode":null,"errorMessage":"orgId is required","messagePattern":"orgId is required","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"app/server/db/locks.go","lineNumber":96,"sourceCode":"\tinitialJitter := time.Duration(rand.Int63n(int64(5000 * time.Microsecond)))\n\n\tselect {\n\tcase <-params.Ctx.Done():\n\t\treturn \"\", params.Ctx.Err()\n\tcase <-time.After(initialJitter):\n\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","sourceCodeStart":78,"sourceCodeEnd":114,"githubUrl":"https://github.com/plandex-ai/plandex/blob/e2d772072efadbe41d2946d97d79be55532dbab5/app/server/db/locks.go#L78-L114","documentation":"lockRepoDB validates LockRepoParams before touching the database: OrgId must be non-empty because every repo lock row is scoped to an org. If it is empty, lockRepoDB returns immediately with \"orgId is required\" without attempting a transaction.","triggerScenarios":"Constructing LockRepoParams without setting OrgId (zero-value struct passed to LockRepo / LockRepoDB), or clearing the field when copying params between calls.","commonSituations":"Refactoring added a new lock call site and the org id wasn't threaded through; org id loaded from a session/config that was empty in a dev environment; partial struct literal omitted OrgId.","solutions":["Populate LockRepoParams.OrgId from the authenticated user's org before calling the lock API.","Add a precondition check or constructor for LockRepoParams that rejects empty OrgId at the call site.","Trace where the empty value originates — usually an unauthenticated request or an org not attached to the plan."],"exampleFix":"// before\nparams := db.LockRepoParams{PlanId: planId, UserId: userId, Scope: db.LockScopeWrite}\n// after\nparams := db.LockRepoParams{OrgId: orgId, PlanId: planId, UserId: userId, Scope: db.LockScopeWrite}\nif orgId == \"\" { return fmt.Errorf(\"cannot lock plan without an org\") }","handlingStrategy":"validation","validationCode":"func validateLockParams(p db.LockRepoParams) error {\n    if p.OrgId == \"\" { return errors.New(\"orgId must be set before locking\") }\n    return nil\n}","typeGuard":"func hasOrgId(p db.LockRepoParams) bool { return strings.TrimSpace(p.OrgId) != \"\" }","tryCatchPattern":"if err := validateLockParams(params); err != nil { return err }\nlockId, err := db.LockRepo(ctx, cancel, params)\nif err != nil { return fmt.Errorf(\"lock failed: %w\", err) }","preventionTips":["Use a constructor (NewLockRepoParams) that requires orgId so zero-value structs are impossible.","Derive orgId from the authenticated session, never from client-supplied body fields.","Add a unit test asserting lock calls with empty orgId fail fast at your boundary."],"tags":["validation","locking","input-validation"],"backgroundTag":"missing-required-argument","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"}