{"record":{"id":"22ddca09a0e8f0cf","repo":"charmbracelet/crush","slug":"panic-s","errorCode":null,"errorMessage":"panic: %s","messagePattern":"panic: (.+?)","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/agent/tools/mcp/init.go","lineNumber":619,"sourceCode":"// Shared by Initialize and Reinitialize so the panic-to-state policy\n// lives in one place. wg, if non-nil, is Done when the attempt finishes\n// (success or failure); Initialize uses it to await startup. The goroutine\n// captures the server's generation at launch so a concurrent teardown\n// invalidates its result rather than letting it register a stale session.\nfunc goInitClient(ctx context.Context, cfg *config.ConfigStore, name string, m config.MCPConfig, wg *sync.WaitGroup) {\n\tgen := currentGen(name)\n\tgo func() {\n\t\tif wg != nil {\n\t\t\tdefer wg.Done()\n\t\t}\n\t\tdefer func() {\n\t\t\tif r := recover(); r != nil {\n\t\t\t\tvar err error\n\t\t\t\tswitch v := r.(type) {\n\t\t\t\tcase error:\n\t\t\t\t\terr = v\n\t\t\t\tcase string:\n\t\t\t\t\terr = fmt.Errorf(\"panic: %s\", v)\n\t\t\t\tdefault:\n\t\t\t\t\terr = fmt.Errorf(\"panic: %v\", v)\n\t\t\t\t}\n\t\t\t\tupdateState(name, StateError, err, nil, Counts{})\n\t\t\t\tslog.Error(\"Panic in MCP client initialization\", \"error\", err, \"name\", name)\n\t\t\t}\n\t\t}()\n\t\tstart := time.Now()\n\t\terr := initClient(ctx, cfg, name, m, gen, cfg.Resolver())\n\t\tslog.Debug(\n\t\t\t\"MCP client initialization finished\",\n\t\t\t\"name\", name,\n\t\t\t\"duration\", time.Since(start).Truncate(time.Millisecond).String(),\n\t\t\t\"error\", err,\n\t\t)\n\t}()\n}\n","sourceCodeStart":601,"sourceCodeEnd":637,"githubUrl":"https://github.com/charmbracelet/crush/blob/7944b8e52225d8805e31eacbf7ef24856b0dfb7a/internal/agent/tools/mcp/init.go#L601-L637","documentation":"This is the panic-recovery path in MCP client initialization. When a goroutine initializing an MCP client panics with a string value, the recover() converts it into an error via fmt.Errorf(\"panic: %s\", v). The MCP server session is then marked StateError and the panic is logged instead of crashing the whole application.","triggerScenarios":"Any panic in the MCP init goroutine where the panic value is a plain string, e.g. panic(\"conn closed\") or a library calling panic with a string. The deferred recover at the top of the init function catches it and formats this message.","commonSituations":"Third-party MCP SDK code panicking on malformed server responses; nil-map or nil-pointer bugs inside custom transports; stdio subprocess dying in a way that makes the client library panic with a string.","solutions":["Check the slog output for 'Panic in MCP client initialization' to get the panic string and stack context","Reproduce with the specific MCP server config to identify which server's init panics","Upgrade the MCP SDK / transport library, the panic is usually inside library code","Validate the server config (command, args, env, url) so the library never receives invalid input"],"exampleFix":"// before: init goroutine panics with a string and crashes\npanic(\"unexpected nil transport\")\n\n// after: this recover already exists; ensure config is validated before starting\nif strings.TrimSpace(cfg.Command) == \"\" { return errors.New(\"empty command\") }","handlingStrategy":"try-catch","validationCode":null,"typeGuard":"func asError(r any) error {\n    switch v := r.(type) {\n    case error: return v\n    case string: return fmt.Errorf(\"panic: %s\", v)\n    default: return fmt.Errorf(\"panic: %v\", v)\n    }\n}","tryCatchPattern":"// the library already recovers; callers should treat StateError sessions as dead\nsess, ok := sessions.Get(name)\nif !ok || sess.State == StateError {\n    slog.Warn(\"MCP session unavailable after panic\", \"name\", name)\n}","preventionTips":["Avoid panic() for error signaling in custom transports and handlers","Keep MCP SDK/transport libraries up to date","Validate server configs before starting clients","Monitor the 'Panic in MCP client initialization' log line"],"tags":["panic","recovery","mcp"],"backgroundTag":"recovered-panic-in-goroutine","analyzedSha":"7944b8e52225d8805e31eacbf7ef24856b0dfb7a","analyzedAt":"2026-08-29T12:48:59.079Z","schemaVersion":2},"datasetVersion":"2026-08-29T17:17:51.833Z"}