go-delve/delve · error
Expression %q is unreadable: %v
Error message
Expression %q is unreadable: %v
What it means
setValue (used both for SetVariable and for copying arguments during function-call injection via funcCallCopyOneArg) refuses to write when the source expression evaluated to an unreadable variable — srcv.Unreadable was set while loading it (bad memory, optimized-out value, unreadable DWARF, etc.). The debugger surfaces the underlying unreadability reason rather than writing garbage to the destination.
Source
Thrown at pkg/proc/eval.go:635
// containing a single pointer field) the type conversion to "interface {}"
// is performed.
// - If srcv and dstv have the same type and are both addressable then the
// contents of srcv are copied byte-by-byte into dstv
func (scope *EvalScope) setValue(dstv, srcv *Variable, srcExpr string) error {
srcv.loadValue(loadSingleValue)
typerr := srcv.isType(dstv.RealType, dstv.Kind)
if _, isTypeConvErr := typerr.(*typeConvErr); isTypeConvErr {
// attempt iface -> eface and ptr-shaped -> eface conversions.
return convertToEface(srcv, dstv)
}
if typerr != nil {
return typerr
}
if srcv.Unreadable != nil {
//lint:ignore ST1005 backwards compatibility
return fmt.Errorf("Expression %q is unreadable: %v", srcExpr, srcv.Unreadable)
}
// Numerical types
switch dstv.Kind {
case reflect.Float32, reflect.Float64:
f, _ := constant.Float64Val(srcv.Value)
return dstv.writeFloatRaw(f, dstv.RealType.Size())
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
n, _ := constant.Int64Val(srcv.Value)
return dstv.writeUint(uint64(n), dstv.RealType.Size())
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
n, _ := constant.Uint64Val(srcv.Value)
return dstv.writeUint(n, dstv.RealType.Size())
case reflect.Bool:
return dstv.writeBool(constant.BoolVal(srcv.Value))
case reflect.Complex64, reflect.Complex128:
real, _ := constant.Float64Val(constant.Real(srcv.Value))
imag, _ := constant.Float64Val(constant.Imag(srcv.Value))View on GitHub (pinned to a23773e6c3)
Solutions
- Verify the source expression evaluates on its own first (EvalExpression); fix or replace it if it is unreadable.
- Use a literal value instead of copying from an unreadable variable (e.g. x = 0 instead of x = y when y is unreadable).
- Rebuild the debuggee without optimizations (-gcflags="all=-N -l") so variables have concrete locations.
- Evaluate in a different frame where the variable is actually live and addressable.
Example fix
// before
dbg.SetVariable("x", "optimizedOutVar") // Expression "optimizedOutVar" is unreadable
// after
val, err := dbg.EvalExpression("optimizedOutVar", cfg)
if err == nil && val.Unreadable == nil {
dbg.SetVariable("x", "optimizedOutVar")
} else {
dbg.SetVariable("x", "0")
} Defensive patterns
Strategy: validation
Validate before calling
val, err := scope.EvalExpression(srcExpr, loadSingleValue)
if err != nil || val == nil || val.Unreadable != nil {
return fmt.Errorf("source expression %q not readable, refusing assignment", srcExpr)
} Type guard
func isReadable(v *proc.Variable) bool { return v != nil && v.Unreadable == nil } Try / catch
if err := scope.SetVariable(name, value); err != nil {
var uv *proc.Variable
if strings.Contains(err.Error(), "is unreadable") {
// fall back to a literal assignment or surface the reason to the user
return fmt.Errorf("cannot copy from %s: %w", value, err)
}
return err
} Prevention
- Evaluate the source expression standalone before using it as an assignment source
- Build without optimizations (-gcflags="all=-N -l") so variables stay readable
- Prefer literal values over variable-to-variable copies in automation scripts
- Check Variable.Unreadable on any variable before using it as a value source
When it happens
Trigger: Calling SetVariable(name, value) or a function-call injection argument copy where the value expression resolves to a variable whose memory cannot be read (srcv.Unreadable != nil after loadValue), e.g. assigning one variable's value to another when the source variable is optimized out or lives in unreadable memory.
Common situations: Assigning from a variable that the compiler optimized into registers and DWARF cannot reconstruct; copying from a variable behind a nil or dangling pointer; source expression in an inlined frame whose location is unavailable; stripped/optimized binaries (-gcflags='-N -l' not used).
Related errors
- can not assign to %s
- can not set variables of type %s (not implemented)
- 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/936cf69a63228692.
Report an issue: GitHub.