go-delve/delve · error

could not recover call injection state for goroutine %d (thr

Error message

could not recover call injection state for goroutine %d (thread %d)

What it means

Delve tracks per-goroutine call-injection state in t.fncallForG[g.ID]. When a thread finishes an injected call, findCallInjectionStateForThread looks up the goroutine's entry; if there is no entry, or the entry's evalStack is nil, the protocol cannot recover what the injected call was doing, so it fails with 'could not recover call injection state'.

Source

Thrown at pkg/proc/fncall.go:1206

		if !callinj.evalStack.callInjectionContinue {
			err := finishEvalExpressionWithCalls(t, g, callinj.evalStack)
			if err != nil {
				return done, err
			}
			done = true
		}
	}
	return done, nil
}

func findCallInjectionStateForThread(t *Target, thread Thread) (*G, *callInjection, error) {
	g, err := GetG(thread)
	if err != nil {
		return nil, nil, fmt.Errorf("could not determine running goroutine for thread %#x currently executing the function call injection protocol: %v", thread.ThreadID(), err)
	}
	fncallLog("findCallInjectionStateForThread thread=%d goroutine=%d", thread.ThreadID(), g.ID)
	notfound := func() error {
		return fmt.Errorf("could not recover call injection state for goroutine %d (thread %d)", g.ID, thread.ThreadID())
	}
	callinj := t.fncallForG[g.ID]
	if callinj != nil {
		if callinj.evalStack == nil {
			return nil, nil, notfound()
		}
		return g, callinj, nil
	}

	// In Go 1.15 and later the call injection protocol will switch to a
	// different goroutine.
	// Here we try to recover the injection goroutine by checking the injection
	// thread.

	for goid, callinj := range t.fncallForG {
		if callinj != nil && callinj.evalStack != nil && callinj.startThreadID != 0 && callinj.startThreadID == thread.ThreadID() {
			t.fncallForG[g.ID] = callinj
			fncallLog("goroutine %d is the goroutine executing the call injection started in goroutine %d", g.ID, goid)

View on GitHub (pinned to a23773e6c3)

Solutions

  1. Avoid issuing other debugger commands (breakpoints, step, switch goroutine) while a function call injection is in progress.
  2. Restart the debug session (dlv restart / re-attach) to clear stale injection state, then re-issue the call expression.
  3. Do not restart or detach/reattach the debugger in the middle of evaluating a call expression.
  4. Update Delve; if reproducible, file an issue with the command sequence that triggered it.
Defensive patterns

Strategy: try-catch

Try / catch

err := continueAndWaitStop()
if err != nil && strings.Contains(err.Error(), "could not recover call injection state") {
    // stale injection state; restart session and re-issue the expression
    return restartAndReissue()
}
return err

Prevention

When it happens

Trigger: A call-injection breakpoint fires for a goroutine whose state was never registered, was already cleaned up (call completed or was cancelled), or whose evalStack was reset — e.g. after a previous step/breakpoint interrupted the injection protocol.

Common situations: User sets a breakpoint inside a function called via an injected call and resumes, corrupting the protocol; debugger restart/reconnect losing in-memory fncallForG while the target still has injection breakpoints; switching goroutines or threads mid-injection; running 'next' or another command while an injected call is in flight.

Related errors


AI-assisted analysis of go-delve/delve@a23773e6c3 (2026-08-31). Data as JSON: /api/errors/538b570a0451dc3b. Report an issue: GitHub.