go-delve/delve · error

could not find watchpoint at address %#x

Error message

could not find watchpoint at address %#x

What it means

On macOS (debugserver/mach exceptions), the thread's watchAddr records the address the thread stopped at. SetCurrentBreakpoint tries to match it against known breakpoints; if it is not a known breakpoint, not a hardcoded breakpoint instruction, and not found in the breakpoint map, Delve reports it cannot resolve the watchpoint at that address.

Source

Thrown at pkg/proc/gdbserial/gdbserver.go:1986

	// t.watchAddr on certain mach kernel versions could contain the address of a
	// software breakpoint, hardcoded breakpoint (e.g. runtime.Breakpoint) or a
	// hardware watchpoint. The mach exception produced by the kernel *should* disambiguate
	// but it doesn't.
	if t.watchAddr > 0 {
		t.CurrentBreakpoint.Breakpoint = t.p.Breakpoints().M[t.watchAddr]
		if t.CurrentBreakpoint.Breakpoint == nil {
			buf := make([]byte, t.BinInfo().Arch.BreakpointSize())
			_, err := t.p.ReadMemory(buf, t.watchAddr)
			isHardcodedBreakpoint := err == nil && (bytes.Equal(t.BinInfo().Arch.BreakpointInstruction(), buf) || bytes.Equal(t.BinInfo().Arch.AltBreakpointInstruction(), buf))
			if isHardcodedBreakpoint {
				// This is a hardcoded breakpoint, ignore.
				// TODO(deparker): There's an optimization here since we will do this
				// again at a higher level to determine if we've stopped at a hardcoded breakpoint.
				// We could set some state here so that we don't do extra work later.
				t.watchAddr = 0
				return nil
			}
			return fmt.Errorf("could not find watchpoint at address %#x", t.watchAddr)
		}
		return nil
	}
	if t.watchReg >= 0 {
		for _, bp := range t.p.Breakpoints().M {
			if bp.WatchType != 0 && bp.HWBreakIndex == uint8(t.watchReg) {
				t.CurrentBreakpoint.Breakpoint = bp
				return nil
			}
		}
	}
	regs, err := t.Registers()
	if err != nil {
		return err
	}
	pc := regs.PC()
	if bp, ok := t.p.FindBreakpoint(pc); ok {
		if t.regs.PC() != bp.Addr {

View on GitHub (pinned to a23773e6c3)

Solutions

  1. Retry the continue; if the breakpoint was cleared concurrently the race typically resolves
  2. Check that no other tool is injecting breakpoint instructions into the target
  3. Update Delve — mach exception watchAddr disambiguation has known fixes on newer macOS versions
  4. If reproducible, capture Delve logs with the watchAddr value and inspect what instruction sits at that address in the target
Defensive patterns

Strategy: try-catch

Validate before calling

// no caller-side pre-check possible; error arises from mach exception disambiguation
isWatchpointErr := func(err error) bool {
    return strings.Contains(err.Error(), "could not find watchpoint at address")
}

Try / catch

err := thread.SetCurrentBreakpoint(false)
if isWatchpointErr(err) {
    // race or foreign trap; re-continue and re-evaluate on next stop
    _ = proc.Continue()
}

Prevention

When it happens

Trigger: A mach exception reports a stop whose address is neither a Delve-registered breakpoint/watchpoint nor a Go hardcoded breakpoint (runtime.Breakpoint) — e.g. a breakpoint cleared between the stop and SetCurrentBreakpoint, or a foreign/trap instruction injected by something else.

Common situations: Clearing breakpoints while threads are concurrently hitting them on macOS; mixing Delve with another debugger/instrumentation injecting INT3s; kernel/dispatch quirks where mach exceptions report software breakpoint addresses ambiguously.

Related errors


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