go-delve/delve · error
Frame %d does not exist in goroutine %d
Error message
Frame %d does not exist in goroutine %d
What it means
ConvertEvalScope builds an EvalScope for a given goroutine ID and stack frame. After walking the goroutine's stack it checks whether the requested frame index actually exists; if the stack has fewer frames than the requested index, it refuses to build a scope and returns this error. It is a guard against out-of-range frame access, not a debugger malfunction.
Source
Thrown at pkg/proc/eval.go:115
if deferCall > 0 {
opts = StacktraceReadDefers
}
var locs []Stackframe
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
}
View on GitHub (pinned to a23773e6c3)
Solutions
- Re-fetch the goroutine's stack (Stacktrace/GoroutineStacktrace) and use a frame index within len(locs)-1 before evaluating.
- Re-acquire the current execution state: get a fresh stack after every stop event instead of reusing a stale frame index.
- Check the goroutine still exists and is stopped (FindGoroutine/valid target) before evaluating in its frames.
- For deferred-call scopes, pass deferCall > 0 only when the frame actually has pending defers.
Example fix
// before
scope, err := debugger.ConvertEvalScope(t, gid, 10, 0)
// after
locs, err := proc.GoroutineStacktrace(t, g, 11, 0)
if err != nil { return err }
frame := 10
if frame >= len(locs) { frame = len(locs) - 1 }
scope, err := debugger.ConvertEvalScope(t, gid, frame, 0) Defensive patterns
Strategy: validation
Validate before calling
locs, err := proc.GoroutineStacktrace(t, g, frame+1, 0)
if err != nil { return err }
if frame < 0 || frame >= len(locs) {
return fmt.Errorf("frame %d unavailable, goroutine %d has %d frames", frame, gid, len(locs))
} Type guard
func frameExists(locs []proc.Stackframe, frame int) bool { return frame >= 0 && frame < len(locs) } Try / catch
scope, err := proc.ConvertEvalScope(t, gid, frame, 0)
if err != nil {
if strings.Contains(err.Error(), "does not exist in goroutine") {
frame = 0 // fall back to topmost frame
scope, err = proc.ConvertEvalScope(t, gid, frame, 0)
}
if err != nil { return err }
} Prevention
- Always fetch a fresh stacktrace after every stop event before referencing frame indices
- Clamp frame indices to the current stack depth returned by Stacktrace
- Never cache frame numbers across continue/step operations
- Check goroutine status (running/dead) before evaluating in its frames
When it happens
Trigger: Calling ConvertEvalScope (directly or via pushLocal from service-level eval APIs, e.g. EvalExpression in a specific frame) with a frame index >= the number of frames in the target goroutine's stack; e.g. frame 5 on a goroutine whose stack only has 3 frames, or referencing a frame after the goroutine has exited and its stack shrunk.
Common situations: IDE/plugins caching a stale frame index after a step or breakpoint hit changed the stack depth; clients storing frame numbers across continuation events; goroutine that finished between listing and evaluation; asking for a deferred call frame on a shallow goroutine stack.
Related errors
- could not decode first frame
- Frame %d only has %d deferred calls
- unable to find function context
- unable to find locals: no debug information present in binar
- no address for escaped variable
AI-assisted analysis of go-delve/delve@a23773e6c3 (2026-08-31).
Data as JSON: /api/errors/cdb1a49b66f78cb7.
Report an issue: GitHub.