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
- Re-run under rr with optimizations disabled (build with -gcflags="-N -l") so return addresses land on decoded instruction boundaries
- Avoid reverse step-out/next over code containing tail calls or hand-written assembly where no CALL precedes the return address
- Re-record the execution trace (rr record) if the binary changed after recording, causing disassembly/PC mismatch
- 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
- Record and debug with unoptimized builds (-gcflags="all=-N -l")
- Avoid reverse step-out over tail calls, assembly, or inlined call sites
- Re-record rr traces whenever the binary is rebuilt
- Use forward stepping when the frame's caller is not ordinary Go code
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
- direction change with internal breakpoints
- can not start a call injection while running backwards
- next while nexting
- not on topmost frame
- Invalid next count
AI-assisted analysis of go-delve/delve@a23773e6c3 (2026-08-31).
Data as JSON: /api/errors/bc9304f55982b6af.
Report an issue: GitHub.