go-delve/delve · error

can not watch %q

Error message

can not watch %q

What it means

SetWatchpoint evaluates the given expression as an AST to find the watched variable's address. If the result has no address (Addr==0), a fake address, or no DWARF type, Delve cannot install a hardware watchpoint on it, so it returns this error naming the expression.

Source

Thrown at pkg/proc/breakpoints.go:734

}

// SetWatchpoint sets a data breakpoint at addr and stores it in the
// process wide break point table.
func (t *Target) SetWatchpoint(logicalID int, scope *EvalScope, expr string, wtype WatchType, cond ast.Expr) (*Breakpoint, error) {
	if (wtype&WatchWrite == 0) && (wtype&WatchRead == 0) {
		return nil, errors.New("at least one of read and write must be set for watchpoint")
	}

	n, err := parser.ParseExpr(expr)
	if err != nil {
		return nil, err
	}
	xv, err := scope.evalAST(n)
	if err != nil {
		return nil, err
	}
	if xv.Addr == 0 || xv.Flags&VariableFakeAddress != 0 || xv.DwarfType == nil {
		return nil, fmt.Errorf("can not watch %q", expr)
	}
	if xv.Unreadable != nil {
		return nil, fmt.Errorf("expression %q is unreadable: %v", expr, xv.Unreadable)
	}
	if xv.Kind == reflect.UnsafePointer || xv.Kind == reflect.Invalid {
		return nil, fmt.Errorf("can not watch variable of type %s", xv.Kind.String())
	}

	// Special handling for interface types
	if xv.Kind == reflect.Interface {
		// For interfaces, we want to watch the data they point to
		// Read the interface to get the data pointer
		_, data, _ := xv.readInterface()
		if xv.Unreadable != nil {
			return nil, fmt.Errorf("error reading interface %q: %v", expr, xv.Unreadable)
		}
		if data == nil {
			return nil, fmt.Errorf("invalid interface %q", expr)

View on GitHub (pinned to a23773e6c3)

Solutions

  1. Watch an addressable variable stored in memory; take its address first if needed (e.g. watch *&x)
  2. Rebuild with -gcflags='all=-N -l' so locals are not optimized into registers and keep a stack address
  3. Ensure the binary retains DWARF debug info (no -w, not stripped)
  4. Restructure code to store the value of interest in a variable before watching it

Example fix

// before
dlv: watch someFunc()
// after
val := someFunc()
watch val  // val is now an addressable memory variable
Defensive patterns

Strategy: validation

Validate before calling

xv, err := scope.EvalExpression(expr, LoadConfig{})
watchable := err == nil && xv.Addr != 0 && xv.DwarfType != nil && xv.Flags&proc.VariableFakeAddress == 0

Type guard

func watchable(v *proc.Variable) bool { return v != nil && v.Addr != 0 && v.DwarfType != nil && v.Flags&proc.VariableFakeAddress == 0 }

Try / catch

bp, err := db.SetWatchpoint(0, 0, expr, Read, []string{})
if err != nil && strings.HasPrefix(err.Error(), "can not watch") {
    return fmt.Errorf("expression %q is not an addressable variable; assign it to a local first", expr)
}

Prevention

When it happens

Trigger: Calling SetWatchpoint with an expression that does not resolve to a concrete memory-resident variable: literals, register-allocated temporaries, composite expressions, package constants, or variables whose type info is missing.

Common situations: Watching an optimized-out local (kept in registers only, no stack address); watching 'x.y.z' where the intermediate was folded by the compiler; watching a constant or function return value instead of an addressable variable; missing DWARF type info due to stripped binary.

Related errors


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