go-delve/delve · error

could not find function at %#x

Error message

could not find function at %#x

What it means

Defer.EvalScope sets scope.PC to d.DwrapPC (the PC inside the deferred wrapper function) and resolves it to a function via PCToLine. If no function is found at that address, Delve refuses to build the scope since symbolization of the defer's target failed. Indicates stale or invalid DwrapPC, or missing symbol info.

Source

Thrown at pkg/proc/stack.go:1057

	}
	return d
}

// EvalScope returns an EvalScope relative to the argument frame of this deferred call.
// The argument frame of a deferred call is stored in memory immediately
// after the deferred header.
func (d *Defer) EvalScope(t *Target, thread Thread) (*EvalScope, error) {
	scope, err := GoroutineScope(t, thread)
	if err != nil {
		return nil, fmt.Errorf("could not get scope: %v", err)
	}

	bi := thread.BinInfo()
	scope.PC = d.DwrapPC
	scope.File, scope.Line, scope.Fn = bi.PCToLine(d.DwrapPC)

	if scope.Fn == nil {
		return nil, fmt.Errorf("could not find function at %#x", d.DwrapPC)
	}

	// The arguments are stored immediately after the defer header struct, i.e.
	// addr+sizeof(_defer).

	if !bi.Arch.usesLR {
		// On architectures that don't have a link register CFA is always the address of the first
		// argument, that's what we use for the value of CFA.
		// For SP we use CFA minus the size of one pointer because that would be
		// the space occupied by pushing the return address on the stack during the
		// CALL.
		scope.Regs.CFA = (int64(d.variable.Addr) + d.variable.RealType.Common().ByteSize)
		scope.Regs.Reg(scope.Regs.SPRegNum).Uint64Val = uint64(scope.Regs.CFA - int64(bi.Arch.PtrSize()))
	} else {
		// On architectures that have a link register CFA and SP have the same
		// value but the address of the first argument is at CFA+ptrSize so we set
		// CFA to the start of the argument frame minus one pointer size.
		scope.Regs.CFA = int64(d.variable.Addr) + d.variable.RealType.Common().ByteSize - int64(bi.Arch.PtrSize())

View on GitHub (pinned to a23773e6c3)

Solutions

  1. Verify the binary loaded in Delve exactly matches the running process (same build/paths)
  2. Re-attach or restart the debug session to refresh defer records
  3. Rebuild the target with full symbols
  4. Update Delve; defer/dwrap parsing changes across Go versions

Example fix

null
Defensive patterns

Strategy: validation

Validate before calling

fn := bi.PCToFunction(dwrapPC); if fn == nil { return errors.New("defer dwrap PC not in loaded binary: binary mismatch") }

Type guard

func deferResolvable(bi *proc.BinaryInfo, d *proc.Defer) bool { fn, _, _ := bi.PCToLine(d.DwrapPC); return fn != nil }

Try / catch

scope, err := d.EvalScope(t, thread); if err != nil && strings.Contains(err.Error(), "could not find function at") { /* flag binary/process mismatch and resync */ }

Prevention

When it happens

Trigger: Calling Defer.EvalScope where d.DwrapPC doesn't map to any known function — defer records created by a different/older binary, corrupted _defer structs, or binary info loaded without the function containing DwrapPC (stripped/mismatched binary).

Common situations: Attached to a process running a different build than the binary Delve loaded; Go runtime version changes altering dwrap layout; corrupted memory reads of the defer chain.

Related errors


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