go-delve/delve · warning
can not write registers
Error message
can not write registers
What it means
After bounds checking, compositeMemory.WriteMemory inspects its pieces; if any piece is a register piece (op.RegPiece) and mem.regs.ChangeFunc is nil, there is no mechanism to write values back into CPU registers, so it returns 'can not write registers'. This protects against silently writing only the cached slice while the live value stays in a register.
Source
Thrown at pkg/proc/mem.go:195
func (mem *compositeMemory) ReadMemory(data []byte, addr uint64) (int, error) {
addr -= mem.base
if addr >= uint64(len(mem.data)) || addr+uint64(len(data)) > uint64(len(mem.data)) {
return 0, errors.New("read out of bounds")
}
copy(data, mem.data[addr:addr+uint64(len(data))])
return len(data), nil
}
func (mem *compositeMemory) WriteMemory(addr uint64, data []byte) (int, error) {
addr -= mem.base
if addr >= uint64(len(mem.data)) || addr+uint64(len(data)) > uint64(len(mem.data)) {
return 0, errors.New("write out of bounds")
}
if mem.regs.ChangeFunc == nil {
for _, piece := range mem.pieces {
if piece.Kind == op.RegPiece {
return 0, errors.New("can not write registers")
}
}
}
copy(mem.data[addr:], data)
curAddr := uint64(0)
donesz := 0
for _, piece := range mem.pieces {
if curAddr < (addr+uint64(len(data))) && addr < (curAddr+uint64(piece.Size)) {
// changed memory interval overlaps current piece
pieceMem := mem.data[curAddr : curAddr+uint64(piece.Size)]
switch piece.Kind {
case op.RegPiece:
oldReg := mem.regs.Reg(piece.Val)
newReg := op.DwarfRegisterFromBytes(pieceMem)
err := mem.regs.ChangeFunc(piece.Val, oldReg.Overwrite(newReg))View on GitHub (pinned to a23773e6c3)
Solutions
- Rebuild the target with -gcflags="all=-N -l" so the variable spills to a stack slot, then set it
- Set a different, memory-located variable or restructure code (copy the value to a stack variable) for debugging
- Avoid variable writes in core-dump sessions; instead, edit code or use live debugging with a backend supporting register writes
Example fix
// before $ go build main.go # i kept in a register (dlv) set i = 10 // can not write registers // after $ go build -gcflags="all=-N -l" main.go (dlv) set i = 10
Defensive patterns
Strategy: fallback
Validate before calling
// Detect register-backed variables before writing: // (dlv) print &i // If the address is not a normal stack address (or the build is optimized // or you are in a core dump), the write will hit registers and fail.
Try / catch
err := setVariable("i", "10")
if err != nil && strings.Contains(err.Error(), "can not write registers") {
log.Printf("variable held in registers; rebuild with -gcflags='all=-N -l' to set it")
return fallbackSkipWrite()
} Prevention
- Build debug binaries with -gcflags="all=-N -l" so locals spill to memory
- Do not attempt variable writes in core-dump sessions
- Assign to stack variables (copies) instead of register-kept parameters
When it happens
Trigger: Assigning to a variable whose value is (partially) held in CPU registers via compositeMemory when the register set has no ChangeFunc — e.g. the 'set' command on an argument or local variable kept in a register, common in optimized code and on architectures/backends without register-write support (e.g. core dump targets).
Common situations: Setting variables in optimized binaries where parameters are register-allocated; attempting writes while debugging a core dump (registers are read-only); backends lacking register modification support.
Related errors
- write out of bounds
- can not change register values of core process
- 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/1ba9179ade4d845c.
Report an issue: GitHub.