{"record":{"id":"60a58754290f2cbc","repo":"go-delve/delve","slug":"thread-stack-trace-returned-error","errorCode":null,"errorMessage":"thread stack trace returned error","messagePattern":"thread stack trace returned error","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"service/debugger/debugger.go","lineNumber":1472,"sourceCode":"\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"disassemble failed with error %w\", err)\n\t\t}\n\t\tfor _, instr := range text {\n\t\t\t// Dynamic functions need to be handled specially as their destination location\n\t\t\t// is not known statically, hence its required to put a breakpoint in order to\n\t\t\t// acquire the address of the function at runtime and we do this via a\n\t\t\t// call back mechanism\n\t\t\tif instr.IsCall() && instr.DestLoc == nil {\n\t\t\t\tdynbp, err := t.SetBreakpoint(0, instr.Loc.PC, proc.NextBreakpoint, nil)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn nil, fmt.Errorf(\"error setting breakpoint inside deferreturn\")\n\t\t\t\t}\n\t\t\t\tdynCallback := func(th proc.Thread, tgt *proc.Target) (bool, error) {\n\t\t\t\t\t// TODO(optimization): Consider using an iterator to avoid materializing\n\t\t\t\t\t// the full stack when we only need frames up to the root function\n\t\t\t\t\trawlocs, err := proc.ThreadStacktrace(tgt, tgt.CurrentThread(), followCalls+2)\n\t\t\t\t\tif err != nil {\n\t\t\t\t\t\treturn false, fmt.Errorf(\"thread stack trace returned error\")\n\t\t\t\t\t}\n\t\t\t\t\t// Since the dynamic function is known only at runtime, the depth is likewise\n\t\t\t\t\t// calculated by referring to the stack and the mechanism is similar to that\n\t\t\t\t\t// used in pkg/terminal/command.go:printTraceOutput\n\t\t\t\t\trootindex := -1\n\t\t\t\t\tfor i := len(rawlocs) - 1; i >= 0; i-- {\n\t\t\t\t\t\tif rawlocs[i].Call.Fn.Name == rootstr {\n\t\t\t\t\t\t\tif rootindex == -1 {\n\t\t\t\t\t\t\t\trootindex = i\n\t\t\t\t\t\t\t\tbreak\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tsdepth := rootindex + 1\n\n\t\t\t\t\tif sdepth+1 > followCalls {\n\t\t\t\t\t\treturn false, nil\n\t\t\t\t\t}","sourceCodeStart":1454,"sourceCodeEnd":1490,"githubUrl":"https://github.com/go-delve/delve/blob/a23773e6c31361e43246bc43a424ee009679b174/service/debugger/debugger.go#L1454-L1490","documentation":"Error produced inside the dynamic-call breakpoint callback installed by traverse. When a runtime breakpoint fires on an unresolved call instruction, the callback takes a full thread stack trace (proc.ThreadStacktrace up to followCalls+2 frames) to locate the root function and compute dynamic call depth; if that stack walk fails, this error is returned and the dynamic call cannot be resolved.","triggerScenarios":"During a continue/trace session with follow-calls enabled: the breakpoint callback fires on a dynamic call site and ThreadStacktrace fails because the goroutine's stack is unreadable or inconsistent — goroutine exiting between breakpoint hit and callback, corrupted stack unwinding info, extremely shallow stack, or the thread died / process resumed unexpectedly.","commonSituations":"Tracing deferreturn-driven calls where the goroutine unwinds quickly; race between process exit and callback execution; cgo or assembly frames that cannot be unwound; core-dump or remote targets where stack reads fail.","solutions":["Treat as transient: re-run the trace/continue — the callback failure aborts only that dynamic-call resolution.","Reduce followCalls depth so fewer dynamic callbacks are armed and stacks are walked shallower.","Ensure the target process is alive and not racing to exit around the traced call (add a sync point/breakpoint after the call).","Check the binary has unwind information (DWARF/frame pointers) for the frames being traced.","Update Delve; the follow-calls dynamic resolution code is under active development."],"exampleFix":"// before\nfuncs, err := client.Functions(\"main\", 8) // deep dynamic tracing, races with goroutine exit\n// after\nfuncs, err := client.Functions(\"main\", 3) // shallower walk, fewer runtime callbacks","handlingStrategy":"retry","validationCode":"// confirm the target and goroutines are still live before tracing\nstate, err := client.GetState()\nif err == nil && state.Exited {\n    return fmt.Errorf(\"process exited; cannot trace dynamic calls\")\n}","typeGuard":null,"tryCatchPattern":"for attempt := 0; attempt < 2; attempt++ {\n    _, err = client.Functions(filter, followCalls)\n    if err == nil || !strings.Contains(err.Error(), \"thread stack trace returned error\") {\n        break\n    }\n    time.Sleep(100 * time.Millisecond) // transient unwind race; retry once\n}","preventionTips":["Retry once — stack unwinds often fail transiently while the goroutine is exiting.","Reduce followCalls so fewer dynamic callbacks walk stacks.","Add a sync point after traced calls so goroutines don't exit mid-trace.","Ensure DWARF/frame-pointer unwind info exists in the binary.","Avoid tracing goroutines that finish instantly (e.g. `go func(){...}()` one-shots)."],"tags":["debugger","stacktrace","callback","follow-calls"],"backgroundTag":"stack-unwind-failed","analyzedSha":"a23773e6c31361e43246bc43a424ee009679b174","analyzedAt":"2026-08-31T15:12:45.221Z","schemaVersion":2},"datasetVersion":"2026-08-31T19:17:28.585Z"}