{"record":{"id":"ef5519632cb3ee0f","repo":"ipfs/kubo","slug":"lcstatstop-stopfunc-was-nil","errorCode":null,"errorMessage":"lcStatStop: stopFunc was nil","messagePattern":"lcStatStop: stopFunc was nil","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"core/node/helpers.go","lineNumber":35,"sourceCode":"func (lcss *lcStartStop) Append(f func() func()) {\n\t// Hooks are guaranteed to run in sequence. If a hook fails to start, its\n\t// OnStop won't be executed.\n\tvar stopFunc func()\n\n\tlcss.LC.Append(fx.Hook{\n\t\tOnStart: func(ctx context.Context) error {\n\t\t\tif ctx.Err() != nil {\n\t\t\t\treturn nil\n\t\t\t}\n\t\t\tstopFunc = f()\n\t\t\treturn nil\n\t\t},\n\t\tOnStop: func(ctx context.Context) error {\n\t\t\tif ctx.Err() != nil {\n\t\t\t\treturn nil\n\t\t\t}\n\t\t\tif stopFunc == nil { // Theoretically this shouldn't ever happen\n\t\t\t\treturn errors.New(\"lcStatStop: stopFunc was nil\")\n\t\t\t}\n\t\t\tstopFunc()\n\t\t\treturn nil\n\t\t},\n\t})\n}\n\nfunc maybeProvide(opt any, enable bool) fx.Option {\n\tif enable {\n\t\treturn fx.Provide(opt)\n\t}\n\treturn fx.Options()\n}\n\n// nolint unused\nfunc maybeInvoke(opt any, enable bool) fx.Option {\n\tif enable {\n\t\treturn fx.Invoke(opt)","sourceCodeStart":17,"sourceCodeEnd":53,"githubUrl":"https://github.com/ipfs/kubo/blob/329838acdfafae224582930457efe80aa217afc0/core/node/helpers.go#L17-L53","documentation":"This is a defensive invariant inside lcStatStop's OnStop hook: the lcStartStop helper registers a stop callback captured in a closure, and if OnStop fires before the constructor ever assigned stopFunc, the guard returns this error. The code comment notes this should theoretically never happen; it indicates a lifecycle wiring bug in the fx app (stop hook registered before/without the start hook setting the variable).","triggerScenarios":"fx invokes the app's OnStop hook while the stopFunc captured variable is still nil — i.e. the lifecycle hook was appended but the start function that assigns stopFunc never ran or has not yet run (e.g. start failed midway or was skipped, then shutdown proceeds).","commonSituations":"A resource whose Start step failed silently or whose Append order in fx was changed during a refactor; daemon shutdown racing a partially constructed node. Practically unobservable in healthy runs.","solutions":["Check for recently changed lifecycle wiring (lc.Append order) in the constructor using this helper — the Start hook must run before Stop can fire","Capture the error in a bug report with the daemon's log level raised (GOLOG_LOG_LEVEL=debug) to see which lifecycle hook sequence triggered it","Upgrade kubo — if this reproduces on an official build it is a bug worth filing at github.com/ipfs/kubo/issues"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"if err := app.Stop(ctx); err != nil && strings.Contains(err.Error(), \"lcStatStop: stopFunc was nil\") {\n    // lifecycle wiring bug: log and file an issue; safe to retry shutdown\n    logger.Errorw(\"lifecycle stop hook fired without start\", \"err\", err)\n}","preventionTips":["Keep lc.Append(start/stop) pairs together and in start-then-stop order in fx constructors","Raise GOLOG_LOG_LEVEL=debug when refactoring node lifecycle code to catch hook-order regressions early"],"tags":["lifecycle","fx","internal"],"backgroundTag":"lifecycle-hook-ordering","analyzedSha":"329838acdfafae224582930457efe80aa217afc0","analyzedAt":"2026-09-03T18:30:52.135Z","contentChangedAt":"2026-09-03T18:30:52.135Z","schemaVersion":2},"datasetVersion":"2026-09-11T00:17:11.886Z"}