{"record":{"id":"175db3e62b3a1fe4","repo":"vitessio/vitess","slug":"failed-to-cleanly-close-cluster-id-s-w","errorCode":null,"errorMessage":"failed to cleanly close cluster (id=%s): %w","messagePattern":"failed to cleanly close cluster \\(id=(.+?)\\): %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"go/vt/vtadmin/cluster/cluster.go","lineNumber":236,"sourceCode":"\t\trec concurrency.AllErrorRecorder\n\t)\n\n\t// First, close any caches, which may have connections to DB or Vtctld\n\t// (N.B. (andrew) when we have multiple caches, we can close them\n\t// concurrently, like we do with the proxies).\n\trec.RecordError(c.schemaCache.Close())\n\n\tfor _, closer := range []io.Closer{c.DB, c.Vtctld} {\n\t\twg.Add(1)\n\t\tgo func(closer io.Closer) {\n\t\t\tdefer wg.Done()\n\t\t\trec.RecordError(closer.Close())\n\t\t}(closer)\n\t}\n\twg.Wait()\n\n\tif rec.HasErrors() {\n\t\treturn fmt.Errorf(\"failed to cleanly close cluster (id=%s): %w\", c.ID, rec.Error())\n\t}\n\n\treturn c.schemaCache.Close()\n}\n\n// ToProto returns a value-copy protobuf equivalent of the cluster.\nfunc (c Cluster) ToProto() *vtadminpb.Cluster {\n\treturn &vtadminpb.Cluster{\n\t\tId:   c.ID,\n\t\tName: c.Name,\n\t}\n}\n\nfunc buildPFlagSlice(flags map[string]string) []string {\n\targs := make([]string, 0, len(flags))\n\tfor k, v := range flags {\n\t\t// The k=v syntax is needed to account for negating boolean flags.\n\t\targs = append(args, \"--\"+k+\"=\"+v)","sourceCodeStart":218,"sourceCodeEnd":254,"githubUrl":"https://github.com/vitessio/vitess/blob/01a25a7d176f94613b8d59d799f438380a8760e4/go/vt/vtadmin/cluster/cluster.go#L218-L254","documentation":"cluster.Close aggregates cleanup errors from all registered closers (DB, Vtctld, topo pools, etc.) via an errors.Group, and if any closer failed it returns this wrapped aggregate error including the cluster id. The schema cache is only closed if all closers succeeded. It indicates one or more resources did not shut down cleanly.","triggerScenarios":"Calling cluster.Close (directly or via vtadmin shutdown) when one of the concurrent closer goroutines returns a non-nil error — e.g. gRPC connection Close error, topo pool close failure.","commonSituations":"Shutdown while RPCs are in flight; already-broken connections erroring on Close; underlying servers returning errors during connection teardown.","solutions":["Inspect the joined error(s) to see which closer failed; it is usually safe to restart vtadmin after fixing the underlying connection issue","Ensure in-flight requests are drained before Close","Check network/topo health if closers repeatedly fail"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"err := cluster.Close(ctx)\nif err != nil {\n    if strings.Contains(err.Error(), \"failed to cleanly close cluster\") {\n        log.Warn(\"cluster shutdown had cleanup errors; inspecting\",\n            slog.Any(\"error\", err), slog.String(\"cluster_id\", cluster.ID))\n        // non-fatal in most shutdown paths; proceed after logging\n        return nil\n    }\n    return err\n}","preventionTips":["Drain in-flight RPCs before closing the cluster","Keep timeouts generous in Close's bounded context so closers aren't cut off","Log and alert on repeated close errors; they often precede connection issues at startup"],"tags":["shutdown","cleanup","resource-close"],"backgroundTag":"graceful-shutdown-failed","analyzedSha":"01a25a7d176f94613b8d59d799f438380a8760e4","analyzedAt":"2026-09-01T17:28:30.605Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}