go-delve/delve · error

failed to disassemble instruction: %w

Error message

failed to disassemble instruction: %w

What it means

Delve's dynamic-tracepoint callback (in traverse, used by eBPF follow-calls tracing) hits an indirect call at runtime and re-disassembles the single instruction at its PC to resolve the call destination. This error wraps any failure from proc.Disassemble for that one-instruction range. Because the static pass already disassembled the whole function successfully, failure here usually means memory at the PC is unreadable at callback time (process exited, memory unmapped) or the architecture backend could not decode the bytes.

Source

Thrown at service/debugger/debugger.go:1501

							}
						}
					}
					sdepth := rootindex + 1

					if sdepth+1 > followCalls {
						return false, nil
					}
					regs, err := th.Registers()
					if err != nil {
						return false, fmt.Errorf("registers inside callback returned err")

					}
					// Disassemble the instruction at the current PC to get the call destination
					pc := instr.Loc.PC
					maxInstLen := uint64(tgt.BinInfo().Arch.MaxInstructionLength())
					disasm, err := proc.Disassemble(t.Memory(), regs, t.Breakpoints(), tgt.BinInfo(), pc, pc+maxInstLen)
					if err != nil {
						return false, fmt.Errorf("failed to disassemble instruction: %w", err)
					}

					// Extract address from the decoded instruction's destination location
					var addr uint64
					if len(disasm) > 0 && disasm[0].DestLoc != nil {
						addr = disasm[0].DestLoc.PC
					} else {
						return false, fmt.Errorf("failed to extract call destination from instruction at PC %#x", pc)
					}
					fn := tgt.BinInfo().PCToFunc(addr)
					if fn == nil {
						return false, fmt.Errorf("PCToFunc returned nil")
					}
					err = createFunctionTracepoints(d, fn.Name, rootstr, followCalls)
					if err != nil {
						return false, fmt.Errorf("error creating tracepoint in function %s", fn.Name)
					}
					dynchildren, err := d.traverse(t, fn, sdepth+1, followCalls, rootstr)

View on GitHub (pinned to a23773e6c3)

Solutions

  1. Re-run trace to rule out a transient failure caused by the target dying mid-trace (check with 'dlv attach' or process status first).
  2. Verify the traced binary is a complete, unstripped Go build (go build without -ldflags="-s -w").
  3. Confirm the eBPF backend supports your GOARCH; try tracing a smaller/caller function to isolate the failing call site.
  4. Reduce --follow-calls depth so fewer indirect call sites get dynamic breakpoints.
  5. If reproducible, file an issue with the PC value and the wrapped inner error from %w.

Example fix

// before (error surfaced with no context of which function)
return false, fmt.Errorf("failed to disassemble instruction: %w", err)
// after (add PC + function context to the wrap)
return false, fmt.Errorf("failed to disassemble instruction at PC %#x in %s: %w", pc, f.Name, err)
Defensive patterns

Strategy: try-catch

Validate before calling

// before invoking dlv trace with eBPF follow-calls
dlvcmd := exec.Command("dlv", "trace", "--backend=ebpf", "--follow-calls=2", "--", "./mybin")
if err := dlvcmd.Run(); err != nil {
	if strings.Contains(fmt.Sprint(err), "failed to disassemble instruction") {
		log.Println("binary may be stripped or unsupported arch; rebuild unstripped for", runtime.GOARCH)
	}
}

Type guard

func isDisasmFailure(err error) bool {
	return err != nil && strings.Contains(err.Error(), "failed to disassemble instruction")
}

Try / catch

err := dlvCmd.Run()
var handled bool
if err != nil {
	switch {
	case isDisasmFailure(err):
		// fall back to non-follow-calls tracing
		handled = retryWithoutFollowCalls()
	case isTransient(err):
		handled = retryWithBackoff(3, time.Second, dlvCmd.Run)
	}
	if !handled {
		log.Fatalf("trace failed: %v", err)
	}
}

Prevention

When it happens

Trigger: Returned from the dynCallback breakpoint callback when proc.Disassemble(t.Memory(), regs, t.Breakpoints(), tgt.BinInfo(), pc, pc+maxInstLen) fails while resolving an indirect call (instr.IsCall() && instr.DestLoc == nil) during 'dlv trace --follow-calls' with the eBPF backend.

Common situations: Target process exited or its memory changed between setting the dynamic breakpoint and hitting it; attaching to a stripped/partially-mapped binary; architectures with limited disassembler support; tracing through cgo or JIT-ed code regions where PC points at non-Go memory.

Related errors


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