go-delve/delve · warning
no address for escaped variable
Error message
no address for escaped variable
What it means
When simpleLocals post-processes '&var' entries (variables that were optimized to escapes and exposed via a pointer pseudo-variable), it dereferences the pointer. If the resulting variable has Addr == 0 and no other unreadable reason, delve cannot locate the escaped variable's storage and marks the variable Unreadable with this error.
Source
Thrown at pkg/proc/eval.go:553
}
depths = append(depths, depth)
}
if len(vars) == 0 {
return vars, nil
}
sort.Stable(&variablesByDepthAndDeclLine{vars, depths})
lvn := map[string]*Variable{} // lvn[n] is the last variable we saw named n
for i, v := range vars {
if name := v.Name; len(name) > 1 && name[0] == '&' {
locationExpr := v.LocationExpr
declLine := v.DeclLine
v = v.maybeDereference()
if v.Addr == 0 && v.Unreadable == nil {
v.Unreadable = errors.New("no address for escaped variable")
}
v.Name = name[1:]
v.Flags |= VariableEscaped
// See https://github.com/go-delve/delve/issues/2049 for details
if locationExpr != nil {
locationExpr.isEscaped = true
v.LocationExpr = locationExpr
}
v.DeclLine = declLine
vars[i] = v
}
if otherv := lvn[v.Name]; otherv != nil {
otherv.Flags |= VariableShadowed
}
lvn[v.Name] = v
}
return vars, nilView on GitHub (pinned to a23773e6c3)
Solutions
- Rebuild with optimizations disabled: go build -gcflags="all=-N -l" so locals stay addressable.
- Step to a line where the variable is live (check DeclLine) and re-run locals.
- Read the variable via print &var or print var at a stop point where the compiler kept it addressable; check v.Unreadable in tooling and degrade gracefully.
- If scripting the API, treat Addr==0/Unreadable entries as unavailable and continue rather than aborting the whole locals list.
Example fix
// before
vars, err := scope.Locals(0)
for _, v := range vars { use(v) }
// after
vars, err := scope.Locals(0)
for _, v := range vars {
if v.Unreadable != nil {
continue // e.g. "no address for escaped variable": optimized out
}
use(v)
} Defensive patterns
Strategy: type-guard
Validate before calling
vars, _ := scope.Locals(0)
usable := []*proc.Variable{}
for _, v := range vars {
if v.Unreadable == nil { usable = append(usable, v) }
} Type guard
func isReadable(v *proc.Variable) bool {
return v != nil && v.Unreadable == nil && !(v.Addr == 0 && v.Kind == reflect.Ptr)
} Try / catch
vars, err := scope.Locals(0)
for _, v := range vars {
if v.Unreadable != nil && strings.Contains(v.Unreadable.Error(), "no address for escaped variable") {
render(v.Name, "<optimized out>")
continue
}
render(v.Name, v.ValueString())
} Prevention
- Debug with -gcflags="all=-N -l" to keep locals addressable
- Inspect variables at lines where they are live (respect DeclLine)
- Treat Unreadable variables as '<optimized out>' in tooling
- Prefer newer delve versions with improved register-ABI variable tracking
When it happens
Trigger: Listing locals where the compiler (register ABI / escape analysis) replaced a variable with &var pointing to heap/stack slots, but the pointer value read as 0 — e.g., variable moved to registers, dead at the current PC, or memory read failed silently.
Common situations: Inspecting variables heavily optimized by newer Go compilers (Go 1.15+ register ABI); inspecting a variable outside its live range; debugging optimized (non -N -l) builds where a local only exists as a dead pointer slot.
Related errors
- unable to find function context
- could not decode first frame
- unable to find locals: no debug information present in binar
- can not assign to function expression
- second slice argument must be empty for maps
AI-assisted analysis of go-delve/delve@a23773e6c3 (2026-08-31).
Data as JSON: /api/errors/2364df5a050d0581.
Report an issue: GitHub.