{"record":{"id":"45b73b08ff220da8","repo":"ipfs/kubo","slug":"s-close-w","errorCode":null,"errorMessage":"%s close: %w","messagePattern":"(.+?) close: %w","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"core/shutdown/close.go","lineNumber":29,"sourceCode":"var closeLog = logging.Logger(\"shutdown\")\n\n// CloseWithCtx runs close in a goroutine and returns when it finishes or\n// when ctx is done, whichever comes first. If ctx fires before close\n// returns, the goroutine is leaked intentionally; the process is about to\n// exit, so the leak is bounded by process lifetime. Logs at ERROR which\n// subsystem failed to close in time so operators see it in journal/docker\n// logs.\nfunc CloseWithCtx(ctx context.Context, name string, close func() error) error {\n\tdone := make(chan error, 1)\n\tstart := time.Now()\n\tgo func() { done <- close() }()\n\tselect {\n\tcase err := <-done:\n\t\treturn err\n\tcase <-ctx.Done():\n\t\tcloseLog.Errorf(\"subsystem %q failed to close within shutdown deadline (after %s): %s\",\n\t\t\tname, time.Since(start), ctx.Err())\n\t\treturn fmt.Errorf(\"%s close: %w\", name, ctx.Err())\n\t}\n}\n","sourceCodeStart":11,"sourceCodeEnd":32,"githubUrl":"https://github.com/ipfs/kubo/blob/329838acdfafae224582930457efe80aa217afc0/core/shutdown/close.go#L11-L32","documentation":"CloseWithCtx runs a subsystem's Close in a goroutine and waits up to the caller's context deadline; if the subsystem does not finish closing in time, it logs and returns '<name> close: <ctx.Err()>' (usually context.DeadlineExceeded). The subsystem may still be closing in the background.","triggerScenarios":"Calling CloseWithCtx with a too-short context while a subsystem (e.g. datastore, blockservice, libp2p host) hangs in Close — open handles, slow flushes, stuck network calls.","commonSituations":"Daemon shutdown timing out because a FUSE mount or datastore flush blocks; tests or scripts using a 1-2 second shutdown timeout on a node with large datastore GC.","solutions":["Increase the shutdown deadline context (e.g. 30s) given to CloseWithCtx","Check shutdown logs for the named subsystem to find what is blocking its Close","Ensure the subsystem's dependencies are closed in correct order (children before parents)","Gracefully stop active operations (downloads, GC, FUSE unmounts) before shutdown"],"exampleFix":"// before\ncloseCtx, cancel := context.WithTimeout(ctx, time.Second)\n// after\ncloseCtx, cancel := context.WithTimeout(ctx, 30*time.Second)","handlingStrategy":"try-catch","validationCode":"// nothing to validate beforehand; ensure subsystems are idle before closing\nif node.IsBusy() { time.Sleep(...) }","typeGuard":null,"tryCatchPattern":"closeCtx, cancel := context.WithTimeout(context.Background(), 30*time.Second)\ndefer cancel()\nif err := CloseWithCtx(closeCtx, node, \"node\"); err != nil {\n    if errors.Is(err, context.DeadlineExceeded) {\n        log.Warn(\"node close exceeded deadline; may still be flushing\")\n    }\n}","preventionTips":["Give shutdown a generous deadline proportional to datastore size","Stop active operations (GC, pins, FUSE) before closing","Check shutdown logs for the subsystem named in the error"],"tags":["shutdown","context-deadline","goroutine"],"backgroundTag":"shutdown-timeout","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"}