{"record":{"id":"3ddff88867ddc6e5","repo":"go-delve/delve","slug":"corrupted-stack-sp-not-monotonically-decreasing","errorCode":null,"errorMessage":"corrupted stack (SP not monotonically decreasing)","messagePattern":"corrupted stack \\(SP not monotonically decreasing\\)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/proc/stack.go","lineNumber":1298,"sourceCode":"\t\t\t\tframes = nil\n\t\t\t\taddRetFrame = false\n\t\t\t\tstage = doneStage\n\t\t\t\treturn false\n\t\t\t}\n\t\tcase lastFrameStage:\n\t\t\tframes = append(frames, fr)\n\t\t\tstage = doneStage\n\t\t\treturn false\n\t\tcase doneStage:\n\t\t\treturn false\n\t\t}\n\t\treturn true\n\t})\n\tif it.Err() != nil {\n\t\treturn nil, it.Err()\n\t}\n\tif nonMonotonicSP {\n\t\treturn nil, errors.New(\"corrupted stack (SP not monotonically decreasing)\")\n\t}\n\tif stage != doneStage {\n\t\treturn nil, errors.New(\"could not find range-over-func closure parent on the stack\")\n\t}\n\tif len(frames)%2 != 0 {\n\t\treturn nil, errors.New(\"incomplete range-over-func stacktrace\")\n\t}\n\tg.readDefers(frames)\n\treturn frames, nil\n}\n\ntype cachedStack struct {\n\tit     *stackIterator\n\tframes []Stackframe\n}\n\ntype stackCacheKey struct {\n\tgoid     int64","sourceCodeStart":1280,"sourceCodeEnd":1316,"githubUrl":"https://github.com/go-delve/delve/blob/a23773e6c31361e43246bc43a424ee009679b174/pkg/proc/stack.go#L1280-L1316","documentation":"This error is produced by rangeFuncStackTrace in pkg/proc/stack.go:1298 when, while walking a goroutine stack to reconstruct the frame chain of a range-over-func (range 2.0) statement, the stack pointer of a successive frame is lower than the previous frame's SP. A valid Go stack must grow monotonically downward, so a non-decreasing SP walk indicates the unwind is unreliable (stack corruption, wrong frame pointers, or corrupted memory). Delve aborts the reconstruction rather than returning a bogus stack.","triggerScenarios":"Happens inside rangeFuncStackTrace (called from setupRangeFrames, StepOut, and next) when it.stacktraceFunc walks frames and detects fr.Regs.SP() < prev.Regs.SP(). This occurs when: (1) the range-over-func closure body was stopped in, but the frame chain (FP/BP/SP) cannot be unwound consistently; (2) the goroutine stack was grown or moved concurrently and stale SP values were cached; (3) memory corruption or reading a thread whose stack was reused; (4) optimizing compiler versions emitting frame layouts Delve cannot model.","commonSituations":"Debugging Go programs that use range-over-func iterators (GOEXPERIMENT=rangefunc or Go 1.23+) with `next`/`stepout` inside a yield closure; debugging an optimized binary where closures were inlined or stack-allocated in ways the DWARF does not describe; attaching to a process that was mid-stack-growth when stopped; old Delve versions combined with newer Go compilers.","solutions":["Retry the operation: stop the target again and re-issue `next`/`stepout`/stack request, since transient states (e.g. mid stack-copy) can produce this.","Disable optimizations when building the debuggee (go build with -gcflags=all='-N -l') so frame layouts and closure metadata match what Delve expects.","Upgrade Delve to the latest version compatible with your Go toolchain; range-over-func support is recent and version-sensitive.","Run with --log-output=debug (debugger log) to capture the SP values of the offending frames and file a Delve issue if reproducible.","If the process was attached after a crash or under memory corruption, verify the program itself is not corrupting its stack (e.g. unsafe/cgo bugs)."],"exampleFix":"// before: next/stepout inside a range-over-func closure crashes the stack walk\n// (nothing to change in user code; build-side fix)\n\n// after: rebuild the debuggee without optimizations before debugging\ngo build -gcflags=\"all=-N -l\" -o app ./cmd/app\ndlv exec ./app","handlingStrategy":"retry","validationCode":"// No pre-call validation API exists; guard at call time in the client/driver.\n// Re-request the stack on a fresh stop instead of caching frames across stops.\nframes, err := dbg.Stacktrace(goroutineID, depth)\nif err != nil && strings.Contains(err.Error(), \"SP not monotonically decreasing\") {\n    // transient/unreliable unwind: re-stop and retry once\n}","typeGuard":null,"tryCatchPattern":"try {\n    frames = client.Stacktrace(goid, depth)\n} catch (err) {\n    if (strings.Contains(err.Error(), \"corrupted stack\")) {\n        time.Sleep(50 * time.Millisecond) // let target settle\n        frames = client.Stacktrace(goid, depth) // single retry on fresh stop\n    } else {\n        return err\n    }\n}","preventionTips":["Build the debuggee unoptimized: -gcflags='all=-N -l'","Keep Delve version matched to your Go toolchain version","Do not cache stack frames across Continue/Stop cycles","Avoid attaching to processes suspected of memory corruption (unsafe/cgo)"],"tags":["debugger","go","stack-unwinding","range-over-func"],"backgroundTag":"stack-unwind-sp-not-monotonic","analyzedSha":"a23773e6c31361e43246bc43a424ee009679b174","analyzedAt":"2026-08-31T15:12:45.221Z","schemaVersion":2},"datasetVersion":"2026-08-31T19:17:28.585Z"}