go-delve/delve · error
could not restore SP: %v
Error message
could not restore SP: %v
What it means
Similarly to PC restoration, Delve restores the saved stack pointer after the injected call completes. When setSP fails the stack pointer cannot be written back, which usually means the thread is no longer writable or alive.
Source
Thrown at pkg/proc/fncall.go:869
}
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))
if err != nil {
fncall.err = fmt.Errorf("could not restore RFLAGS register: %v", err)
}
}
return true
View on GitHub (pinned to a23773e6c3)
Solutions
- Confirm the process is alive and re-attach if needed
- Retry the 'call' command in a fresh stop
- Check ptrace permissions (e.g. /proc/sys/kernel/yama/ptrace_scope) for native debugging
- Restart the debug session — corrupted SP is unsafe to continue from
Defensive patterns
Strategy: try-catch
Validate before calling
// Check ptrace works on the host before debugging: // cat /proc/sys/kernel/yama/ptrace_scope (0 or with CAP_SYS_PTRACE is safest)
Try / catch
err := dbg.CallFunction(scope, fn, args)
if err != nil && strings.Contains(err.Error(), "could not restore SP") {
// SP corrupted: detach and restart the session
}
Prevention
- Ensure ptrace is permitted (yama scope, container privileges)
- Do not let the target crash during call injection
- Re-attach on remote-connection drops
- Restart the session after any SP-restore failure
When it happens
Trigger: funcCallStep in the restore-registers phase calls setSP(thread, sp) and the backend write fails.
Common situations: Process crash during the injected call; ptrace failures (permissions, seccomp); remote session dropped; stack pointer computed against a stale register snapshot.
Related errors
- could not restore registers: %v
- could not restore PC: %v
- could not restore RFLAGS register: %v
- can not change register values of core process
- read out of bounds
AI-assisted analysis of go-delve/delve@a23773e6c3 (2026-08-31).
Data as JSON: /api/errors/22c9c12223f45ae9.
Report an issue: GitHub.