go-delve/delve · error

could not set pc

Error message

could not set pc

What it means

On darwin/amd64, nativeThread.setPC calls the Mach wrapper set_pc on the thread act; any kern_return other than KERN_SUCCESS is collapsed into the opaque message 'could not set pc'. The kernel refused to change the instruction pointer of the thread.

Source

Thrown at pkg/proc/native/registers_darwin_amd64.go:122

	return 0
}

// TLS returns the value of the register
// that contains the location of the thread
// local storage segment.
func (r *Regs) TLS() uint64 {
	return r.gsBase
}

func (r *Regs) GAddr() (uint64, bool) {
	return 0, false
}

// SetPC sets the RIP register to the value specified by `pc`.
func (thread *nativeThread) setPC(pc uint64) error {
	kret := C.set_pc(thread.os.threadAct, C.uint64_t(pc))
	if kret != C.KERN_SUCCESS {
		return fmt.Errorf("could not set pc")
	}
	return nil
}

// SetReg changes the value of the specified register.
func (thread *nativeThread) SetReg(regNum uint64, reg *op.DwarfRegister) error {
	if regNum != regnum.AMD64_Rip {
		return fmt.Errorf("changing register %d not implemented", regNum)
	}
	return thread.setPC(reg.Uint64Val)
}

func (r *Regs) Get(n int) (uint64, error) {
	reg := x86asm.Reg(n)
	const (
		mask8  = 0x000f
		mask16 = 0x00ff
		mask32 = 0xffff

View on GitHub (pinned to a23773e6c3)

Solutions

  1. Check the process is still alive before attempting setPC; treat thread-death races as expected failures
  2. Re-attach if task/port rights were invalidated (e.g. after the target execed)
  3. Run delve with proper entitlements/permissions on macOS (SIP constraints, developer mode)
  4. Log the kern_return code from set_pc to distinguish KERN_INVALID_ARGUMENT vs KERN_TERMINATED for accurate handling

Example fix

// before
kret := C.set_pc(thread.os.threadAct, C.uint64_t(pc))
if kret != C.KERN_SUCCESS {
	return fmt.Errorf("could not set pc")
}
// after
kret := C.set_pc(thread.os.threadAct, C.uint64_t(pc))
if kret != C.KERN_SUCCESS {
	return fmt.Errorf("could not set pc to 0x%x: kern_return=%d", pc, kret)
}
Defensive patterns

Strategy: try-catch

Validate before calling

// before setPC on darwin
if !procAlive(dbp) || !isThreadAlive(dbp, thread.ID) {
	return fmt.Errorf("thread gone; cannot set pc")
}

Type guard

func isSetPCErr(err error) bool {
	return err != nil && strings.Contains(err.Error(), "could not set pc")
}

Try / catch

err := thread.setPC(pc)
if isSetPCErr(err) {
	// likely thread/process died mid-step; re-sync debugger state
	dbp.reattachOrDrop()
	return err
}

Prevention

When it happens

Trigger: Setting a new PC during step/restart (e.g. after a breakpoint hit, 'continue' from a breakpoint skipping, or reverse-step machinery) when the thread act is invalid — the thread/process terminated — or Mach privileges were lost.

Common situations: Race where the debuggee exits while delve is relocating the PC off a breakpoint; macOS hardening (SIP/hardened runtime) interfering with task-for-pid; stale thread act after exec.

Related errors


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