go-delve/delve · error

Frame %d only has %d deferred calls

Error message

Frame %d only has %d deferred calls

What it means

When ConvertEvalScope is called with deferCall > 0, the scope is made relative to the deferCall-th deferred call of the given frame. The stack is walked with StacktraceReadDefers; if the frame has fewer deferred calls than requested (deferCall-1 >= len(locs[frame].Defers)), this error is returned because that deferred call does not exist.

Source

Thrown at pkg/proc/eval.go:120

	if g != nil {
		if g.Thread != nil {
			threadID = g.Thread.ThreadID()
		}
		locs, err = GoroutineStacktrace(dbp, g, frame+1, opts)
	} else {
		locs, err = ThreadStacktrace(dbp, ct, frame+1)
	}
	if err != nil {
		return nil, err
	}

	if frame >= len(locs) {
		return nil, fmt.Errorf("Frame %d does not exist in goroutine %d", frame, gid)
	}

	if deferCall > 0 {
		if deferCall-1 >= len(locs[frame].Defers) {
			return nil, fmt.Errorf("Frame %d only has %d deferred calls", frame, len(locs[frame].Defers))
		}

		d := locs[frame].Defers[deferCall-1]
		if d.Unreadable != nil {
			return nil, d.Unreadable
		}

		return d.EvalScope(dbp, ct)
	}

	return FrameToScope(dbp, dbp.Memory(), g, threadID, locs[frame:]...), nil
}

// FrameToScope returns a new EvalScope for frames[0].
// If frames has at least two elements all memory between
// frames[0].Regs.SP() and frames[1].Regs.CFA will be cached.
// Otherwise all memory between frames[0].Regs.SP() and frames[0].Regs.CFA
// will be cached.

View on GitHub (pinned to a23773e6c3)

Solutions

  1. Enumerate the frame's Defers via a stacktrace with StacktraceReadDefers and pick deferCall within 1..len(Defers).
  2. Remember deferCall is 1-based: use deferCall = index+1 of the desired defer, not the raw index.
  3. Re-read defers at the current stop point; a defer may already have run since it was listed.
  4. Fall back to deferCall = 0 (normal frame scope) when the frame has no pending defers.

Example fix

// before
scope, err := proc.ConvertEvalScope(t, gid, frame, 3)
// after
locs, _ := proc.GoroutineStacktrace(t, g, frame+1, proc.StacktraceReadDefers)
deferCall := 0
if len(locs) > frame && len(locs[frame].Defers) >= 3 { deferCall = 3 }
scope, err := proc.ConvertEvalScope(t, gid, frame, deferCall)
Defensive patterns

Strategy: validation

Validate before calling

locs, err := proc.GoroutineStacktrace(t, g, frame+1, proc.StacktraceReadDefers)
if err != nil { return err }
if deferCall < 1 || len(locs) <= frame || deferCall > len(locs[frame].Defers) {
    return fmt.Errorf("frame %d has %d defers, requested %d", frame, len(locs[frame].Defers), deferCall)
}

Type guard

func deferCallExists(locs []proc.Stackframe, frame, deferCall int) bool {
    return frame >= 0 && frame < len(locs) && deferCall >= 1 && deferCall <= len(locs[frame].Defers)
}

Try / catch

scope, err := proc.ConvertEvalScope(t, gid, frame, deferCall)
if err != nil && strings.Contains(err.Error(), "deferred calls") {
    scope, err = proc.ConvertEvalScope(t, gid, frame, 0) // fall back to normal frame scope
}

Prevention

When it happens

Trigger: ConvertEvalScope(dbp, gid, frame, deferCall) where frame's Defers slice has fewer entries than deferCall; e.g. asking for the 2nd deferred call when the frame only registered one defer, or requesting deferCall > 0 on a frame with no pending defers at all.

Common situations: Client UIs listing deferred calls from an older stop event and re-evaluating after execution moved past a deferred function; miscounting defers (defers are listed innermost-first, index is 1-based); evaluating a defer after its frame returned.

Related errors


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