go-delve/delve · error
could not restore LR: %v
Error message
could not restore LR: %v
What it means
On arm64/ppc64le/loong64, Delve's call injection protocol stashes the original Link Register value on the stack and must restore it once the injected call completes. callInjectionComplete2 reads the saved LR from the thread's stack via readUintRaw at regs.SP(). If that memory read fails, fncall.err is set to "could not restore LR: %v" and the call injection aborts, leaving the target's register/stack state at risk of corruption.
Source
Thrown at pkg/proc/fncall.go:979
// possible is to ignore it and hope it didn't matter.
stack.callInjectionContinue = true
fncallLog("unknown value of protocol register %#x", regval)
}
return false
}
func callInjectionComplete2(callScope *EvalScope, bi *BinaryInfo, fncall *functionCallState, regs Registers, thread Thread) {
// Store the stack span of the currently running goroutine (which in Go >=
// 1.15 might be different from the original injection goroutine) so that
// later on we can use it to perform the escapeCheck
if threadg, _ := GetG(thread); threadg != nil {
callScope.callCtx.stacks = append(callScope.callCtx.stacks, threadg.stack)
}
if bi.Arch.Name == "arm64" || bi.Arch.Name == "ppc64le" || bi.Arch.Name == "loong64" {
oldlr, err := readUintRaw(thread.ProcessMemory(), regs.SP(), int64(bi.Arch.PtrSize()))
if err != nil {
fncall.err = fmt.Errorf("could not restore LR: %v", err)
return
}
if err = setLR(thread, oldlr); err != nil {
fncall.err = fmt.Errorf("could not restore LR: %v", err)
return
}
}
}
func (scope *EvalScope) evalCallInjectionSetTarget(op *evalop.CallInjectionSetTarget, stack *evalStack, thread Thread) {
fncall := stack.fncallPeek()
if !fncall.hasDebugPinner && (fncall.fn == nil || fncall.receiver != nil || fncall.closureAddr != 0) {
stack.err = funcCallEvalFuncExpr(scope, stack, fncall)
if stack.err != nil {
return
}
}
stack.pop() // target function, consumed by funcCallEvalFuncExpr either above or in evalop.CallInjectionStartView on GitHub (pinned to a23773e6c3)
Solutions
- Check the wrapped cause: if it is a memory-read error, verify the target process is still alive (threads command).
- Restart or re-attach to the target process; this error usually indicates lost protocol state that cannot be repaired in-session.
- Avoid injecting calls into functions that panic or exit the process; guard expressions before calling.
- Update Delve to the latest version to get protocol fixes for your architecture and Go version.
- Retry on a stable breakpoint where SP matches the goroutine's current stack.
Defensive patterns
Strategy: retry
Validate before calling
// before injecting, confirm thread is alive and stack is mapped
thr, err := proc.FindThread(dbg, tid)
if err != nil { return err } // thread gone -> do not inject
_, err = thr.Registers()
if err != nil { return err } // cannot even read regs -> skip injection Try / catch
res, err := dbg.StepInstruction / CallFunction(...)
if err != nil && strings.Contains(err.Error(), "could not restore LR:") {
// saved LR unreadable or register write failed: session state is lost
// only recovery is re-attach/restart; surface to user, do not resume
} Prevention
- On arm64/ppc64le/loong64 treat call-injection failures as session-fatal; plan re-attach.
- Don't inject calls that can os.Exit or crash the process mid-protocol.
- Inject at stable breakpoints, not during signals or mid-stepping.
- Ensure debugger has register-write privileges (ptrace_scope, sudo on Linux/macOS).
- Update Delve; arch-specific LR save/restore bugs get fixed regularly.
When it happens
Trigger: Completing a function call injection on arm64, ppc64le, or loong64 when readUintRaw(thread.ProcessMemory(), regs.SP(), ptrSize) fails: the thread exited, SP points to unmapped/corrupt memory, or the process died between the call finishing and the completion step.
Common situations: Target process crashes or exits during the injected call; debugging a core dump or remote session where memory at SP is not readable; Go runtime moved the goroutine to a different stack mid-call (stack growth) so the saved LR is no longer at the recorded SP.
Related errors
- could not get panic: %v
- could not set call receiver: %v
- function %s not found
- unexpected return type for mallocgc call: %v
- could not determine running goroutine for thread %#x current
AI-assisted analysis of go-delve/delve@a23773e6c3 (2026-08-31).
Data as JSON: /api/errors/056f4b9339a7f789.
Report an issue: GitHub.