{"record":{"id":"ba08154f84bd1a35","repo":"go-delve/delve","slug":"corrupted-defer-list-sp-decreased","errorCode":null,"errorMessage":"corrupted defer list: SP decreased","messagePattern":"corrupted defer list: SP decreased","errorType":"exception","errorClass":"errSPDecreased","httpStatus":null,"severity":"error","filePath":"pkg/proc/stack.go","lineNumber":1022,"sourceCode":"\t\t\t\t\t\tbreak\n\t\t\t\t\t}\n\t\t\t\t\tif len(d.rangefunc) > maxRangeFuncDefers {\n\t\t\t\t\t\t// 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.\n\t\t\t\t\t\tbreak\n\t\t\t\t\t}\n\t\t\t\t\thd = hd.link\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n\n// errSPDecreased is used when (*Defer).Next detects a corrupted linked\n// list, specifically when after following a link pointer the value of SP\n// decreases rather than increasing or staying the same (the defer list is a\n// FIFO list, nodes further down the list have been added by function calls\n// further down the call stack and therefore the SP should always increase).\nvar errSPDecreased = errors.New(\"corrupted defer list: SP decreased\")\n\n// Next returns the next defer in the linked list\nfunc (d *Defer) Next() *Defer {\n\tif d.link == nil {\n\t\treturn nil\n\t}\n\td.link.load(true)\n\tif d.link.SP < d.SP {\n\t\td.link.Unreadable = errSPDecreased\n\t}\n\treturn d.link\n}\n\nfunc (d *Defer) topdefer() *Defer {\n\tif len(d.rangefunc) > 0 {\n\t\treturn d.rangefunc[0]\n\t}\n\treturn d","sourceCodeStart":1004,"sourceCodeEnd":1040,"githubUrl":"https://github.com/go-delve/delve/blob/a23773e6c31361e43246bc43a424ee009679b174/pkg/proc/stack.go#L1004-L1040","documentation":"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).","triggerScenarios":"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.","commonSituations":"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.","solutions":["Update delve to a version matching the target's Go toolchain version","Re-read the goroutine state while the target is stopped consistently (ensure the process is halted)","Verify the target Go version is supported by this delve build","If reading a core dump, confirm the core was produced by the same binary/build"],"exampleFix":"// before\ndlv attach <pid> // delve built for Go 1.21, target built with Go 1.24\n// after\ngo install github.com/go-delve/delve/cmd/dlv@latest && dlv attach <pid>","handlingStrategy":"try-catch","validationCode":"// ensure target is stopped and delve matches Go version\nout, _ := exec.Command(\"go\", \"version\", \"-m\", binaryPath).Output()\ncheckDelveSupportsGoVersion(string(out))","typeGuard":null,"tryCatchPattern":"frames, err := stacktraceWithDefers(g)\nif err != nil && strings.Contains(err.Error(), \"corrupted defer list\") {\n    log.Printf(\"defer list corrupted for goroutine %d; skipping defer analysis\", g.ID)\n    return stacktraceWithoutDefers(g)\n}","preventionTips":["Keep delve version in sync with the target's Go toolchain version","Only read runtime structures while the target is fully stopped","Rebuild or re-dump corrupted core files from the same binary build"],"tags":["runtime","defer","corruption","stacktrace"],"backgroundTag":"corrupted-defer-list","analyzedSha":"a23773e6c31361e43246bc43a424ee009679b174","analyzedAt":"2026-08-31T15:12:45.221Z","schemaVersion":2},"datasetVersion":"2026-08-31T19:17:28.585Z"}