go-delve/delve · error
could not restore registers: %v
Error message
could not restore registers: %v
What it means
At the end of the function-call protocol the runtime asks Delve to restore all registers (except PC/SP) it saved before the call. This error is raised when thread.RestoreRegisters fails on the target thread, meaning the debuggee's register state could not be put back, leaving the thread in a possibly corrupted state.
Source
Thrown at pkg/proc/fncall.go:863
}
// get error from top of the stack and return it to user
errvar, err := readStackVariable(p, thread, regs, archoff, "string", LoadFullValue())
if err != nil {
fncall.err = fmt.Errorf("could not get precheck error reason: %v", err)
break
}
errvar.Name = "err"
fncall.err = fmt.Errorf("%v", constant.StringVal(errvar.Value))
case debugCallRegCompleteCall: // 0
p.fncallForG[callScope.g.ID].startThreadID = 0
case debugCallRegRestoreRegisters: // 16
// runtime requests that we restore the registers (all except pc and sp),
// this is also the last step of the function call protocol.
pc, sp := regs.PC(), regs.SP()
if err := thread.RestoreRegisters(fncall.savedRegs); err != nil {
fncall.err = fmt.Errorf("could not restore registers: %v", err)
}
if err := setPC(thread, pc); err != nil {
fncall.err = fmt.Errorf("could not restore PC: %v", err)
}
if err := setSP(thread, sp); err != nil {
fncall.err = fmt.Errorf("could not restore SP: %v", err)
}
fncallLog("stepping thread %d", thread.ThreadID())
if err := stepInstructionOut(callScope.callCtx.grp, p, thread, fncall.debugCallName, fncall.debugCallName); err != nil {
fncall.err = fmt.Errorf("could not step out of %s: %v", fncall.debugCallName, err)
}
if bi.Arch.Name == "amd64" {
// The tail of debugCallV2 corrupts the state of RFLAGS, we must restore
// it one extra time after stepping out of it.
// See https://github.com/go-delve/delve/issues/2985 and
// TestCallInjectionFlagCorruption
rflags := bi.Arch.RegistersToDwarfRegisters(0, fncall.savedRegs).Uint64Val(regnum.AMD64_Rflags)
err := thread.SetReg(regnum.AMD64_Rflags, op.DwarfRegisterFromUint64(rflags))View on GitHub (pinned to a23773e6c3)
Solutions
- Check the target process is still alive before issuing 'call'
- Reconnect / restart the debug session — the thread state may be unrecoverable
- Retry the call on a stable backend (native instead of core)
- Update OS/kernel or Delve if RestoreRegisters fails persistently on your platform
Defensive patterns
Strategy: try-catch
Validate before calling
// Before 'call', verify the process is alive:
// if dbg.Target().Process().Exited() { restart session } Try / catch
err := dbg.CallFunction(scope, fn, args)
if err != nil && strings.Contains(err.Error(), "could not restore registers") {
// thread state may be corrupted: restart the debug session
}
Prevention
- Avoid killing/terminating the process mid-call
- Use the native backend rather than core dumps for call injection
- Keep ptrace permissions correct (yama/ptrace_scope)
- Treat any register-restore failure as session-fatal and restart
When it happens
Trigger: funcCallStep handles debugCallRegRestoreRegisters (16) and the backend's RestoreRegisters call returns an error (thread died, ptrace failure, unsupported register set).
Common situations: The debugged process exited or was killed mid-call; native backend ptrace failures on Linux; gdbserial connection dropped during the call; core-dump backend which cannot write registers.
Related errors
- could not restore PC: %v
- could not restore SP: %v
- could not restore RFLAGS register: %v
- wrong number of bytes for register %s (%d)
- changing register %d not implemented
AI-assisted analysis of go-delve/delve@a23773e6c3 (2026-08-31).
Data as JSON: /api/errors/c68ec1d4fdddb9db.
Report an issue: GitHub.