{"record":{"id":"7e17def731c974bf","repo":"go-delve/delve","slug":"ctx-variable-not-found","errorCode":null,"errorMessage":"ctx variable not found","messagePattern":"ctx variable not found","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"pkg/proc/stack_sigtramp.go","lineNumber":45,"sourceCode":"\t\treturn nil\n\t}\n\n\tderef := func(v *Variable) (uint64, error) {\n\t\tv.loadValue(loadSingleValue)\n\t\tif v.Unreadable != nil {\n\t\t\treturn 0, fmt.Errorf(\"could not dereference %s: %v\", v.Name, v.Unreadable)\n\t\t}\n\t\tif len(v.Children) < 1 {\n\t\t\treturn 0, fmt.Errorf(\"could not dereference %s (no children?)\", v.Name)\n\t\t}\n\t\tlogger.Debugf(\"%s address is %#x\", v.Name, v.Children[0].Addr)\n\t\treturn v.Children[0].Addr, nil\n\t}\n\n\tgetctxaddr := func() (uint64, error) {\n\t\tctxvar := findvar(\"ctx\")\n\t\tif ctxvar == nil {\n\t\t\treturn 0, errors.New(\"ctx variable not found\")\n\t\t}\n\t\taddr, err := deref(ctxvar)\n\t\tif err != nil {\n\t\t\treturn 0, err\n\t\t}\n\t\treturn addr, nil\n\t}\n\n\tswitch bi.GOOS {\n\tcase \"windows\":\n\t\tepvar := findvar(\"ep\")\n\t\tif epvar == nil {\n\t\t\treturn nil, errors.New(\"ep variable not found\")\n\t\t}\n\t\tepaddr, err := deref(epvar)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}","sourceCodeStart":27,"sourceCodeEnd":63,"githubUrl":"https://github.com/go-delve/delve/blob/a23773e6c31361e43246bc43a424ee009679b174/pkg/proc/stack_sigtramp.go#L27-L63","documentation":"readSigtrampgoContext (pkg/proc/stack_sigtramp.go:45) recovers the interrupted thread's register state when the debugger stops inside the signal handler trampoline (runtime.sigtrampgo). It looks up the local variable `ctx` in the sigtrampgo frame's scope via DWARF variable information and dereferences it to find the ucontext. This error means no variable named `ctx` could be found in that frame, so Delve cannot reconstruct the registers and unwind out of the signal handler.","triggerScenarios":"Triggered when the debuggee is stopped in runtime.sigtrampgo (signal delivery: SIGSEGV, SIGBUS, or Go's own signal-based preemption) on Linux/FreeBSD/Darwin, and scope.Locals(0, \"ctx\") returns nothing named `ctx`. Causes: (1) Go runtime built without a resolvable DWARF entry for ctx (highly optimized runtime, ctx kept only in registers); (2) binary info mismatch — the sigtrampgo frame is attributed to a different/mismatched binary; (3) the frame is not actually sigtrampgo (misattributed PC), so `ctx` never exists in scope; (4) stripped or partial DWARF in the runtime portion of the binary.","commonSituations":"Debugging a program that received SIGSEGV and Delve stops the thread inside the signal trampoline; stepping while Go's async preemption signal (SIGURG) lands; core-dump or attach debugging where the stopped PC is in sigtrampgo; debugging a binary whose runtime was compiled with -ldflags=\"-s -w\" or otherwise stripped symbols.","solutions":["Keep DWARF symbols in the binary: do not pass -ldflags=\"-s -w\"; rebuild with default flags or -gcflags='all=-N -l' for debugging.","Verify the binary being debugged matches the running process (dlv attach) — a stale BinInfo makes the sigtrampgo frame unresolvable; relaunch the session.","Update Delve; newer Go runtimes change where sigtrampgo stores ctx, and Delve must be updated to match your Go version.","As a fallback, read thread registers directly (e.g. `regs` command) instead of relying on the signal-context reconstruction; or continue past the signal.","If you only need the fault location, inspect the SIGSEGV address / goroutine state rather than unwinding through sigtrampgo."],"exampleFix":"// before: stripped binary stops in sigtrampgo, ctx is not in DWARF\ngo build -ldflags=\"-s -w\" -o app .\n\n// after: keep debug info so the signal context can be recovered\ngo build -o app .   # or: go build -gcflags=\"all=-N -l\" -o app .","handlingStrategy":"try-catch","validationCode":"// Before debugging, ensure the binary retains DWARF info:\n// go tool buildid <binary> && go version -m <binary>\n// and confirm it was NOT built with -ldflags=\"-s -w\".","typeGuard":null,"tryCatchPattern":"try {\n    frames = client.Stacktrace(threadID, depth)\n} catch (err) {\n    if (strings.Contains(err.Error(), \"ctx variable not found\")) {\n        // stopped in sigtrampgo without recoverable signal context;\n        // fall back to raw register state or continue past the signal\n        regs, regErr := client.ListThreadRegs(threadID, false)\n        _ = regs\n        _ = regErr\n    } else {\n        return err\n    }\n}","preventionTips":["Never strip symbols (-ldflags=\"-s -w\") on binaries you will debug","Match Delve version to Go version before debugging signal-heavy code","Verify attach target binary matches the launched binary byte-for-byte","Handle SIGSEGV in tests so the debugger does not stop inside sigtrampgo"],"tags":["debugger","go","signal-handling","dwarf"],"backgroundTag":"debug-variable-not-found","analyzedSha":"a23773e6c31361e43246bc43a424ee009679b174","analyzedAt":"2026-08-31T15:12:45.221Z","schemaVersion":2},"datasetVersion":"2026-08-31T19:17:28.585Z"}