go-delve/delve · error
could not get precheck error reason: %v
Error message
could not get precheck error reason: %v
What it means
During the debugCallV2 protocol, the Go runtime writes a human-readable reason string on the stack when it must abort an injected call. Delve failed to read that stack variable (of type 'string'), so it wraps the read failure with this message instead of the underlying runtime reason. The actual function-call error is then hidden behind this diagnostic.
Source
Thrown at pkg/proc/fncall.go:849
fnname = loc.Fn.Name
}
}
fncallLog("function call interrupt gid=%d (original) thread=%d regval=%#x (PC=%#x in %s %s:%d)", callScope.g.ID, thread.ThreadID(), regval, pc, fnname, loc.File, loc.Line)
}
switch regval {
case debugCallRegPrecheckFailed: // 8
stack.callInjectionContinue = true
archoff := uint64(0)
if bi.Arch.Name == "arm64" || bi.Arch.Name == "loong64" {
archoff = 8
} else if bi.Arch.Name == "ppc64le" {
archoff = 40
}
// 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)
}View on GitHub (pinned to a23773e6c3)
Solutions
- Retry the 'call' command — transient memory-read failures often succeed on a second attempt
- Check the target process is alive and not already crashed
- Ensure the binary has full debug info (not stripped)
- Upgrade Delve and/or Go: protocol offsets changed between versions
Defensive patterns
Strategy: retry
Validate before calling
// Before 'call', confirm the target is stoppable and readable: // dlv: state, err := dbg.ProcessState(); check state.Exited == false // and that the binary is not stripped: file <binary> | grep 'not stripped'
Try / catch
err := dbg.CallFunction(scope, fn, args)
if err != nil && strings.Contains(err.Error(), "could not get precheck error reason") {
// retry once after confirming process alive
}
Prevention
- Use non-stripped binaries so the debug-call stub variables are visible
- Match Delve version to the target Go version (protocol offsets change)
- Ensure the process is alive before injecting calls
- Retry transient stack-read failures once
When it happens
Trigger: funcCallStep receives debugCallRegCheckPrecondition/other failure status from the runtime, and readStackVariable fails while reading the 'string' reason variable from the top of the stack (bad stack pointer, unreadable memory, missing registers).
Common situations: Attaching to a stripped or foreign binary whose DWARF for the debug-call stubs is missing; process memory unreadable because the target crashed mid-protocol; architecture offset mismatches (archoff) on ppc64le/arm64/amd64.
Related errors
- not implemented: %s
- could not step out of %s: %v
- could not decode first frame
- unable to find function context
- unable to find locals: no debug information present in binar
AI-assisted analysis of go-delve/delve@a23773e6c3 (2026-08-31).
Data as JSON: /api/errors/43af5eea0959f8ad.
Report an issue: GitHub.