plandex-ai/plandex · critical

panic in UpdateContexts: %v\n%s

Error message

panic in UpdateContexts: %v\n%s

What it means

UpdateContexts spawns a goroutine per context ID; if that goroutine panics, the deferred recover() logs the panic with a stack trace and forwards a wrapped "panic in UpdateContexts" error onto errCh so the caller surfaces it instead of crashing the process. runtime.Goexit() then stops the goroutine to avoid double-sending to the channel.

Source

Thrown at app/server/db/context_helpers_update.go:145

		return nil, fmt.Errorf("total context body size exceeds limit (size %.2f MB, limit %d MB)", float64(totalBodySize)/1024/1024, int(shared.MaxContextBodySize)/1024/1024)
	}

	var updatedContexts []*shared.Context

	numFiles := 0
	numUrls := 0
	numTrees := 0
	numMaps := 0

	var mu sync.Mutex
	errCh := make(chan error, len(*req))

	for id, params := range *req {
		go func(id string, params *shared.UpdateContextParams) {
			defer func() {
				if r := recover(); r != nil {
					log.Printf("panic in UpdateContexts: %v\n%s", r, debug.Stack())
					errCh <- fmt.Errorf("panic in UpdateContexts: %v\n%s", r, debug.Stack())
					runtime.Goexit() // don't allow outer function to continue and double-send to channel
				}
			}()
			var context *Context
			if _, ok := contextsById[id]; ok {
				context = contextsById[id]
			} else {
				var err error
				context, err = GetContext(orgId, planId, id, true, true)

				if err != nil {
					errCh <- fmt.Errorf("error getting context: %v", err)
					return
				}
				// log.Println("Got context", context.Id, "numTokens", context.NumTokens)
			}

			mu.Lock()

View on GitHub (pinned to e2d772072e)

Solutions

  1. Read the logged stack trace from the error message to find the panicking line and fix the underlying nil/invalid-data bug.
  2. Validate UpdateContextParams (non-nil Body, valid fields) before calling UpdateContexts.
  3. Check DB rows for the offending context ID for corruption or missing fields.
  4. Add defensive nil checks in the goroutine's update path upstream of the panic site.
  5. Retry the update after fixing, since errCh delivery fails the whole call.

Example fix

// before
params := req[id]
context.NumTokens = countTokens(params.Body) // panics if params or Body nil
// after
if params == nil || params.Body == "" {
    errCh <- fmt.Errorf("invalid params for context %s", id)
    return
}
context.NumTokens = countTokens(params.Body)
Defensive patterns

Strategy: try-catch

Validate before calling

for id, p := range req {
    if p == nil || p.Body == "" { return fmt.Errorf("nil/empty params for context %s", id) }
}

Type guard

func validUpdateParams(p *shared.UpdateContextParams) bool { return p != nil && p.Body != "" }

Try / catch

_, err := UpdateContexts(orgId, planId, branch, &req)
if err != nil && strings.HasPrefix(err.Error(), "panic in UpdateContexts") {
    log.Fatalf("goroutine panic: %v", err) // inspect the embedded stack trace
}

Prevention

When it happens

Trigger: Any panic inside the per-context update goroutine — e.g. nil-pointer dereference on a context field, out-of-range index, or a callee (token counting, DB layer) panicking on malformed params.Body or unexpected context state.

Common situations: Corrupt or nil context rows fetched from the DB; a nil Body or nil map field in UpdateContextParams; library-level panic (e.g. tokenizer) on unusual content; concurrent map access introduced by a code change.

Related errors


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