{"record":{"id":"c951151fe6561014","repo":"plandex-ai/plandex","slug":"panic-in-operation-v-s","errorCode":null,"errorMessage":"panic in operation: %v\n%s","messagePattern":"panic in operation: (.+?)\n(.+?)","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"app/server/db/queue.go","lineNumber":252,"sourceCode":"\t\t\t\t\t\tif locksVerboseLogging {\n\t\t\t\t\t\t\tlog.Printf(\"[Queue] Operation %s (%s) context canceled\", op.id, op.reason)\n\t\t\t\t\t\t}\n\t\t\t\t\t\top.done <- op.ctx.Err()\n\t\t\t\t\tdefault:\n\t\t\t\t\t\tif locksVerboseLogging {\n\t\t\t\t\t\t\tlog.Printf(\"[Queue] Starting operation %s (%s)\", op.id, op.reason)\n\t\t\t\t\t\t}\n\t\t\t\t\t\t// actually do the operation\n\n\t\t\t\t\t\tvar opErr error\n\n\t\t\t\t\t\tfunc() {\n\t\t\t\t\t\t\tdefer func() {\n\t\t\t\t\t\t\t\tpanicErr := recover()\n\t\t\t\t\t\t\t\tif panicErr != nil {\n\t\t\t\t\t\t\t\t\tlog.Printf(\"[Queue] Panic in operation %s (%s): %v\", op.id, op.reason, panicErr)\n\t\t\t\t\t\t\t\t\tlog.Printf(\"[Queue] Stack trace: %s\", string(debug.Stack()))\n\t\t\t\t\t\t\t\t\topErr = fmt.Errorf(\"panic in operation: %v\\n%s\", panicErr, string(debug.Stack()))\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\tif opErr != nil && op.scope == LockScopeWrite && op.clearRepoOnErr {\n\t\t\t\t\t\t\t\t\tif locksVerboseLogging {\n\t\t\t\t\t\t\t\t\t\tlog.Printf(\"[Queue] Operation %s (%s) failed with error, marking for rollback: %v\",\n\t\t\t\t\t\t\t\t\t\t\top.id, op.reason, opErr)\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\tneedsRollback = true\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}()\n\n\t\t\t\t\t\t\tif locksVerboseLogging {\n\t\t\t\t\t\t\t\tlog.Printf(\"[Queue] Executing operation %s (%s)\", op.id, op.reason)\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\topErr = op.op(repo)\n\t\t\t\t\t\t\tif locksVerboseLogging {\n\t\t\t\t\t\t\t\tif opErr != nil {\n\t\t\t\t\t\t\t\t\tlog.Printf(\"[Queue] Operation %s (%s) failed with error: %v\", op.id, op.reason, opErr)","sourceCodeStart":234,"sourceCodeEnd":270,"githubUrl":"https://github.com/plandex-ai/plandex/blob/e2d772072efadbe41d2946d97d79be55532dbab5/app/server/db/queue.go#L234-L270","documentation":"When the queue executes an operation, a deferred recover() captures any panic raised by the operation body, logs it with a stack trace, and converts it into `panic in operation: %v\\n%s`. The opErr is then applied to the operation and, for write-scope ops with clearRepoOnErr, the repo is marked for rollback so partial writes don't persist.","triggerScenarios":"A queued operation's callback panics — nil dereference, index out of range, or any unhandled panic inside the op function executed by the queue worker.","commonSituations":"Buggy operation callbacks touching uninitialized state, panics from lower-layer helpers on unexpected data, races on shared repo state between concurrent ops.","solutions":["Read the attached stack trace to find the panicking line in the operation callback","Fix the underlying nil/bounds bug in the op function","Note that write-scope ops with clearRepoOnErr will be rolled back — verify repo state after a panic","Move preconditions/validation into the op before touching shared state to avoid panics"],"exampleFix":"// before\nrepo.Update(key, mutate(val))\n// after\nif val == nil {\n    opErr = fmt.Errorf(\"cannot mutate nil value for key %s\", key)\n    return\n}\nrepo.Update(key, mutate(val))","handlingStrategy":"try-catch","validationCode":"// before queuing, ensure the op callback's inputs are non-nil\nif key == \"\" || repo == nil {\n    return errors.New(\"invalid operation: key and repo must be set\")\n}","typeGuard":null,"tryCatchPattern":"err := <-op.done\nif err != nil && strings.Contains(err.Error(), \"panic in operation\") {\n    log.Printf(\"queued op panicked; stack embedded in error: %v\", err)\n    // write-scope ops with clearRepoOnErr were marked for rollback; reload repo state\n}","preventionTips":["Validate op inputs before enqueueing to avoid nil-deref panics","Keep operation callbacks pure with respect to shared repo state","Set clearRepoOnErr on write ops so partial writes are rolled back","Alert on '[Queue] Panic in operation' logs to catch buggy callbacks early"],"tags":["go","panic","queue","concurrency","rollback"],"backgroundTag":"recovered-panic","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"}