go-delve/delve · error

direction change with internal breakpoints

Error message

direction change with internal breakpoints

What it means

The gdbserial backend throws this exported error when a direction change (switching between forward and reverse execution of a recorded process, e.g. with rr) is requested while internal breakpoints are still inserted. Reverse-execution machinery cannot safely flip direction with those breakpoints in place, so the request is refused until they are cleared.

Source

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

		stdout, err := exec.Command("xcode-select", "--print-path").Output()
		if err != nil {
			return ""
		}

		xcodePath := strings.TrimSpace(string(stdout))
		if xcodePath == "" {
			return ""
		}

		// xcode-select prints the path to the active Developer directory, which is typically a sibling to SharedFrameworks.
		return filepath.Join(xcodePath, "..", debugserverXcodeRelativeExecutablePath)
	}(),
}

// ErrDirChange is returned when trying to change execution direction
// while there are still internal breakpoints set.
var ErrDirChange = errors.New("direction change with internal breakpoints")

// ErrStartCallInjectionBackwards is returned when trying to start a call
// injection while the recording is being run backwards.
var ErrStartCallInjectionBackwards = errors.New("can not start a call injection while running backwards")

var checkCanUnmaskSignalsOnce sync.Once
var canUnmaskSignalsCached bool

// gdbProcess implements proc.Process using a connection to a debugger stub
// that understands Gdb Remote Serial Protocol.
type gdbProcess struct {
	bi       *proc.BinaryInfo
	regnames *gdbRegnames
	conn     gdbConn

	threads       map[int]*gdbThread
	currentThread *gdbThread

View on GitHub (pinned to a23773e6c3)

Solutions

  1. Clear the relevant breakpoints (or continue to remove internal ones) before changing direction
  2. Retry the direction change after the internal breakpoints are drained (e.g. after a continue/step completes)
  3. Compare errors.Is(err, gdbserial.ErrDirChange) in clients and schedule the change after the next resume
  4. Restart the recorded session (rr replay from start) if breakpoint state cannot be cleared

Example fix

// before
err := grp.ChangeDirection(proc.Backward) // may fail with ErrDirChange
// after
if err := grp.ChangeDirection(proc.Backward); errors.Is(err, gdbserial.ErrDirChange) {
    // clear breakpoints / continue, then retry direction change
}
Defensive patterns

Strategy: try-catch

Validate before calling

// only change direction when no internal breakpoints are pending
if len(breakpointsWithInternalState()) == 0 {
    err := grp.ChangeDirection(proc.Backward)
    _ = err
}

Type guard

func isErrDirChange(err error) bool {
    return errors.Is(err, gdbserial.ErrDirChange)
}

Try / catch

if err := grp.ChangeDirection(proc.Backward); errors.Is(err, gdbserial.ErrDirChange) {
    // continue once to clear internal breakpoints, then retry
}

Prevention

When it happens

Trigger: `rev`/`restart`/ChangeDirection on an rr-recorded target while user or internal breakpoints set by Delve remain; toggling direction right after stepping set internal breakpoints.

Common situations: Time-travel debugging with Mozilla rr and switching to reverse before breakpoints are removed; scripted sessions alternating direction per command.

Related errors


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