plandex-ai/plandex · error

panic in operation: %v %s

Error message

panic in operation: %v
%s

What it means

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.

Source

Thrown at app/server/db/queue.go:252

						if locksVerboseLogging {
							log.Printf("[Queue] Operation %s (%s) context canceled", op.id, op.reason)
						}
						op.done <- op.ctx.Err()
					default:
						if locksVerboseLogging {
							log.Printf("[Queue] Starting operation %s (%s)", op.id, op.reason)
						}
						// actually do the operation

						var opErr error

						func() {
							defer func() {
								panicErr := recover()
								if panicErr != nil {
									log.Printf("[Queue] Panic in operation %s (%s): %v", op.id, op.reason, panicErr)
									log.Printf("[Queue] Stack trace: %s", string(debug.Stack()))
									opErr = fmt.Errorf("panic in operation: %v\n%s", panicErr, string(debug.Stack()))
								}

								if opErr != nil && op.scope == LockScopeWrite && op.clearRepoOnErr {
									if locksVerboseLogging {
										log.Printf("[Queue] Operation %s (%s) failed with error, marking for rollback: %v",
											op.id, op.reason, opErr)
									}
									needsRollback = true
								}
							}()

							if locksVerboseLogging {
								log.Printf("[Queue] Executing operation %s (%s)", op.id, op.reason)
							}
							opErr = op.op(repo)
							if locksVerboseLogging {
								if opErr != nil {
									log.Printf("[Queue] Operation %s (%s) failed with error: %v", op.id, op.reason, opErr)

View on GitHub (pinned to e2d772072e)

Solutions

  1. Read the attached stack trace to find the panicking line in the operation callback
  2. Fix the underlying nil/bounds bug in the op function
  3. Note that write-scope ops with clearRepoOnErr will be rolled back — verify repo state after a panic
  4. Move preconditions/validation into the op before touching shared state to avoid panics

Example fix

// before
repo.Update(key, mutate(val))
// after
if val == nil {
    opErr = fmt.Errorf("cannot mutate nil value for key %s", key)
    return
}
repo.Update(key, mutate(val))
Defensive patterns

Strategy: try-catch

Validate before calling

// before queuing, ensure the op callback's inputs are non-nil
if key == "" || repo == nil {
    return errors.New("invalid operation: key and repo must be set")
}

Try / catch

err := <-op.done
if err != nil && strings.Contains(err.Error(), "panic in operation") {
    log.Printf("queued op panicked; stack embedded in error: %v", err)
    // write-scope ops with clearRepoOnErr were marked for rollback; reload repo state
}

Prevention

When it happens

Trigger: 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.

Common situations: Buggy operation callbacks touching uninitialized state, panics from lower-layer helpers on unexpected data, races on shared repo state between concurrent ops.

Related errors


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