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

  1. Verify the source expression evaluates on its own first (EvalExpression); fix or replace it if it is unreadable.
  2. Use a literal value instead of copying from an unreadable variable (e.g. x = 0 instead of x = y when y is unreadable).
  3. Rebuild the debuggee without optimizations (-gcflags="all=-N -l") so variables have concrete locations.
  4. 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

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


AI-assisted analysis of go-delve/delve@a23773e6c3 (2026-08-31). Data as JSON: /api/errors/936cf69a63228692. Report an issue: GitHub.