// Package safego provides a goroutine launcher that recovers panics instead // of letting them crash the whole process. // // Go's default behavior for a panic in ANY goroutine — not just the one // serving an HTTP request, which net/http recovers automatically per // request — is to take down the entire process. This codebase runs several // long-lived or unattended background goroutines (the nomos auto- // continuation worker, resumed chat turns, async execution dispatch, the SSE // event listener) that do real work — JSON parsing of model/tool output, // map/slice indexing — with no operator watching. Before this package, a // single edge case in any of them (a malformed tool result, an unexpected // nil) would crash nomos or the api process outright, taking down every // concurrently-running task or request, not just the one that hit it. package safego import ( "log/slog" "runtime/debug" ) // Go runs fn in a new goroutine. A panic inside fn is recovered and logged // (with a stack trace) instead of crashing the process. label identifies the // goroutine in logs — use something a reader can trace back to the call // site, e.g. "nomos:continuation-worker" or "mcp:executeApprovedViaAPI". func Go(label string, fn func()) { go func() { defer func() { if r := recover(); r != nil { slog.Error("panic recovered in background goroutine", "goroutine", label, "panic", r, "stack", string(debug.Stack())) } }() fn() }() }