go-delve/delve · error

could not decode first frame

Error message

could not decode first frame

What it means

ThreadScope builds an EvalScope for evaluating expressions in the context of a paused OS thread. It requests a 1-frame stack trace; if the trace decodes to zero frames, delve cannot determine where the thread stopped and throws this error. It is thrown when ThreadStacktrace succeeds but returns no frames, so no PC/Fn context exists to evaluate against.

Source

Thrown at pkg/proc/eval.go:166

		maxaddr = uint64(frames[0].Regs.CFA)
	}
	if maxaddr > minaddr && maxaddr-minaddr < maxFramePrefetchSize {
		thread = cacheMemory(thread, minaddr, int(maxaddr-minaddr))
	}

	s := &EvalScope{Location: frames[0].Call, Regs: frames[0].Regs, Mem: thread, g: g, BinInfo: t.BinInfo(), target: t, frameOffset: frames[0].FrameOffset(), threadID: threadID}
	s.PC = frames[0].lastpc
	return s
}

// ThreadScope returns an EvalScope for the given thread.
func ThreadScope(t *Target, thread Thread) (*EvalScope, error) {
	locations, err := ThreadStacktrace(t, thread, 1)
	if err != nil {
		return nil, err
	}
	if len(locations) < 1 {
		return nil, errors.New("could not decode first frame")
	}
	return FrameToScope(t, thread.ProcessMemory(), nil, thread.ThreadID(), locations...), nil
}

// GoroutineScope returns an EvalScope for the goroutine running on the given thread.
func GoroutineScope(t *Target, thread Thread) (*EvalScope, error) {
	locations, err := ThreadStacktrace(t, thread, 1)
	if err != nil {
		return nil, err
	}
	if len(locations) < 1 {
		return nil, errors.New("could not decode first frame")
	}
	g, err := GetG(thread)
	if err != nil {
		return nil, err
	}
	threadID := 0

View on GitHub (pinned to a23773e6c3)

Solutions

  1. Use GoroutineScope(t, thread) instead, which resolves the goroutine running on the thread and often succeeds where the raw thread frame decode yields nothing.
  2. Retry after the target stops again (continue and hit a breakpoint); transient states during attach often resolve once runtime metadata is loaded.
  3. Verify the binary has DWARF debug info (not stripped) so stack decoding can produce frames.
  4. Enumerate valid threads with process threads / ListThreads and pick a thread that has a resolvable goroutine.

Example fix

// before
scope, err := proc.ThreadScope(t, thread)
// after
scope, err := proc.GoroutineScope(t, thread)
if err != nil {
    // thread has no decodable frames; skip or retry after next stop
    return err
}
Defensive patterns

Strategy: fallback

Validate before calling

locs, err := proc.ThreadStacktrace(t, thread, 1)
if err != nil || len(locs) < 1 {
    // scope will fail; use goroutine-based scope or skip thread
    return nil
}

Type guard

func threadHasFrames(t *proc.Target, th proc.Thread) bool {
    locs, err := proc.ThreadStacktrace(t, th, 1)
    return err == nil && len(locs) >= 1
}

Try / catch

scope, err := proc.ThreadScope(t, thread)
if err != nil && strings.Contains(err.Error(), "could not decode first frame") {
    scope, err = proc.GoroutineScope(t, thread)
}
if err != nil { return err }

Prevention

When it happens

Trigger: Calling ThreadScope on a thread whose stack cannot be decoded: thread stopped in code without frame metadata, thread just created/killed between attach and stack walk, or a corrupted/unreadable stack for that thread ID.

Common situations: Attaching to a process where a thread is stopped in a syscall stub, signal trampoline, or early runtime startup before G frames exist; debugging stripped or partially-loaded binaries; racing with thread exit during attach.

Related errors


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