go-delve/delve · error

could not find CALL instruction for address %#x in %s

Error message

could not find CALL instruction for address %#x in %s

What it means

When stepping out in reverse (reverse step-out / reverse next), Delve must find the CALL instruction immediately preceding the return address in the caller's function disassembly. If it disassembles the function text and no instruction's PC equals the frame's return address (or no preceding CALL exists), it cannot place the reverse-step breakpoint and returns this error.

Source

Thrown at pkg/proc/target_exec.go:1492

	return deferpc, nil
}

// findCallInstrForRet returns the PC address of the CALL instruction
// immediately preceding the instruction at ret.
func findCallInstrForRet(p Process, mem MemoryReadWriter, ret uint64, fn *Function) (uint64, error) {
	text, err := disassemble(mem, nil, p.Breakpoints(), p.BinInfo(), fn.Entry, fn.End, false)
	if err != nil {
		return 0, err
	}
	var prevInstr AsmInstruction
	for _, instr := range text {
		if instr.Loc.PC == ret {
			return prevInstr.Loc.PC, nil
		}
		prevInstr = instr
	}
	return 0, fmt.Errorf("could not find CALL instruction for address %#x in %s", ret, fn.Name)
}

// stepOutReverse sets a breakpoint on the CALL instruction that created the current frame, this is either:
//   - the CALL instruction immediately preceding the return address of the
//     current frame
//   - the return address of the current frame if the current frame was
//     created by a runtime.deferreturn run
//   - the return address of the runtime.gopanic frame if the current frame
//     was created by a panic
//
// This function is used to implement reversed StepOut
func stepOutReverse(p *Target, topframe, retframe Stackframe, sameGCond ast.Expr) error {
	curthread := p.CurrentThread()
	selg := p.SelectedGoroutine()

	if selg != nil && selg.Thread != nil {
		curthread = selg.Thread
	}

View on GitHub (pinned to a23773e6c3)

Solutions

  1. Re-run under rr with optimizations disabled (build with -gcflags="-N -l") so return addresses land on decoded instruction boundaries
  2. Avoid reverse step-out/next over code containing tail calls or hand-written assembly where no CALL precedes the return address
  3. Re-record the execution trace (rr record) if the binary changed after recording, causing disassembly/PC mismatch
  4. Fall back to forward stepping (non-reverse) for frames in optimized or assembly code

Example fix

// before: reverse step-out over optimized code
//   dlv: (stepout -) with binary built with optimizations
// after: rebuild for deterministic reverse stepping
//   go build -gcflags="all=-N -l" && rr record ./app && dlv connect :2345
Defensive patterns

Strategy: fallback

Try / catch

err := doReverseStepOut()
if err != nil && strings.Contains(err.Error(), "could not find CALL instruction") {
    // fall back to forward stepping from the current frame
    return doStepOut()
}

Prevention

When it happens

Trigger: Calling StepOut in reverse (or reverse Next crossing a frame boundary) via findCallInstrForRet, where the return address 'ret' of the current frame does not appear as an instruction PC within the disassembly text of fn.

Common situations: Reverse debugging (RecordGeneratebackwards via rr) over functions whose call sites were inlined or optimized so the return address does not match a disassembled instruction boundary; tail-call optimized frames; stale or mis-decoded instruction text.

Related errors


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