go-delve/delve · error · errSPDecreased

corrupted defer list: SP decreased

Error message

corrupted defer list: SP decreased

What it means

errSPDecreased is returned while following the runtime defer linked list when the SP of a subsequent defer node is lower than the previous one. Since defers are pushed FIFO with SP monotonically increasing down the list, a decreasing SP means the list is corrupted (bad pointers, stale memory).

Source

Thrown at pkg/proc/stack.go:1022

						break
					}
					if len(d.rangefunc) > maxRangeFuncDefers {
						// We don't have a way to know for sure that we haven't gone completely off-road while loading this list so limit it to an arbitrary maximum size.
						break
					}
					hd = hd.link
				}
			}
		}
	}
}

// errSPDecreased is used when (*Defer).Next detects a corrupted linked
// list, specifically when after following a link pointer the value of SP
// decreases rather than increasing or staying the same (the defer list is a
// FIFO list, nodes further down the list have been added by function calls
// further down the call stack and therefore the SP should always increase).
var errSPDecreased = errors.New("corrupted defer list: SP decreased")

// Next returns the next defer in the linked list
func (d *Defer) Next() *Defer {
	if d.link == nil {
		return nil
	}
	d.link.load(true)
	if d.link.SP < d.SP {
		d.link.Unreadable = errSPDecreased
	}
	return d.link
}

func (d *Defer) topdefer() *Defer {
	if len(d.rangefunc) > 0 {
		return d.rangefunc[0]
	}
	return d

View on GitHub (pinned to a23773e6c3)

Solutions

  1. Update delve to a version matching the target's Go toolchain version
  2. Re-read the goroutine state while the target is stopped consistently (ensure the process is halted)
  3. Verify the target Go version is supported by this delve build
  4. If reading a core dump, confirm the core was produced by the same binary/build

Example fix

// before
dlv attach <pid> // delve built for Go 1.21, target built with Go 1.24
// after
go install github.com/go-delve/delve/cmd/dlv@latest && dlv attach <pid>
Defensive patterns

Strategy: try-catch

Validate before calling

// ensure target is stopped and delve matches Go version
out, _ := exec.Command("go", "version", "-m", binaryPath).Output()
checkDelveSupportsGoVersion(string(out))

Try / catch

frames, err := stacktraceWithDefers(g)
if err != nil && strings.Contains(err.Error(), "corrupted defer list") {
    log.Printf("defer list corrupted for goroutine %d; skipping defer analysis", g.ID)
    return stacktraceWithoutDefers(g)
}

Prevention

When it happens

Trigger: Iterating defers during stack tracing or panic/unwind analysis (Defer.Next) on a goroutine whose defer linked list contains garbage — e.g. reading a half-updated runtime structure, wrong runtime layout for the Go version, or corrupted stack memory.

Common situations: Attaching to a Go binary whose version's runtime struct layout differs from what delve expects; reading defers of a goroutine running concurrently while the target mutates them; corrupted core dumps.

Related errors


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