go-delve/delve · error
PCToFunc returned nil
Error message
PCToFunc returned nil
What it means
Once the destination address is extracted from the instruction, Delve maps it back to a Go function with BinInfo.PCToFunc to name and trace it. This error is returned when PCToFunc finds no function covering that address. The call target is real code but outside any known Go function — typically runtime traps, cgo/C code, or text not covered by debug info.
Source
Thrown at service/debugger/debugger.go:1513
}
// 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)
if err != nil {
return false, fmt.Errorf("error calling traverse on dynamic children")
}
for _, child := range dynchildren {
err := createFunctionTracepoints(d, child, rootstr, followCalls)
if err != nil {
return false, fmt.Errorf("error creating tracepoint in function %s", child)
}
}
return false, nil
}
for _, dynBrklet := range dynbp.Breaklets {View on GitHub (pinned to a23773e6c3)
Solutions
- Check whether the target address is in C/cgo code (dlv: 'disassemble <addr>' or nm/objdump); skip such targets — eBPF uprobes on Go functions cannot trace them.
- Rebuild the binary unstripped (no -s -w) so the function table is complete.
- Confirm you are tracing a Go binary built with the same toolchain Delve indexed; stale binaries cause lookup misses.
- Extend skip-lists (like the existing runtime.* filter) to also skip unresolved destinations instead of erroring.
Example fix
// before
fn := tgt.BinInfo().PCToFunc(addr)
if fn == nil {
return false, fmt.Errorf("PCToFunc returned nil")
}
// after (skip non-Go destinations such as cgo trampolines)
fn := tgt.BinInfo().PCToFunc(addr)
if fn == nil {
log.Debugf("call destination %#x is not a Go function; skipping", addr)
return false, nil
} Defensive patterns
Strategy: validation
Validate before calling
// before tracing, confirm the binary exposes Go symbols
f, err := elf.Open(binPath)
if err != nil { log.Fatal(err) }
syms, err := f.Symbols()
if err != nil || len(syms) == 0 {
log.Fatal("no symbol table: PCToFunc will fail for call destinations")
}
f.Close() Type guard
func isUnresolvedDestination(err error) bool {
return err != nil && strings.Contains(err.Error(), "PCToFunc returned nil")
} Try / catch
if err := runTrace(); err != nil {
if isUnresolvedDestination(err) {
log.Println("call target is cgo/C or runtime stub; excluding it from follow-calls")
return runTrace(TraceOpts{Skip: unresolvedTargets})
}
return err
} Prevention
- Do not strip Go binaries that will be traced.
- Expect cgo boundaries to be untraceable with eBPF follow-calls; exclude them.
- Match the binary under test to the one Delve indexed (same build).
- Filter runtime.* targets in your trace patterns to avoid stub destinations.
When it happens
Trigger: dynCallback resolved disasm[0].DestLoc.PC to an address for which tgt.BinInfo().PCToFunc(addr) returned nil while creating follow-calls tracepoints.
Common situations: Indirect calls into cgo/C shared libraries (no Go func entries); calls into runtime assembler stubs excluded from the function map; stripped binary or missing symbol table so PCToFunc lookups fail; PLT/thunk trampolines on some platforms.
Related errors
- failed to disassemble instruction: %w
- error calling traverse on dynamic children
- ebpf composite memory: %w
- package %s has no function %s
- traverse failed with error %w
AI-assisted analysis of go-delve/delve@a23773e6c3 (2026-08-31).
Data as JSON: /api/errors/bd245967fa6a4cf6.
Report an issue: GitHub.