vitessio/vitess · warning

failed to cleanly close cluster (id=%s): %w

Error message

failed to cleanly close cluster (id=%s): %w

What it means

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.

Source

Thrown at go/vt/vtadmin/cluster/cluster.go:236

		rec concurrency.AllErrorRecorder
	)

	// First, close any caches, which may have connections to DB or Vtctld
	// (N.B. (andrew) when we have multiple caches, we can close them
	// concurrently, like we do with the proxies).
	rec.RecordError(c.schemaCache.Close())

	for _, closer := range []io.Closer{c.DB, c.Vtctld} {
		wg.Add(1)
		go func(closer io.Closer) {
			defer wg.Done()
			rec.RecordError(closer.Close())
		}(closer)
	}
	wg.Wait()

	if rec.HasErrors() {
		return fmt.Errorf("failed to cleanly close cluster (id=%s): %w", c.ID, rec.Error())
	}

	return c.schemaCache.Close()
}

// ToProto returns a value-copy protobuf equivalent of the cluster.
func (c Cluster) ToProto() *vtadminpb.Cluster {
	return &vtadminpb.Cluster{
		Id:   c.ID,
		Name: c.Name,
	}
}

func buildPFlagSlice(flags map[string]string) []string {
	args := make([]string, 0, len(flags))
	for k, v := range flags {
		// The k=v syntax is needed to account for negating boolean flags.
		args = append(args, "--"+k+"="+v)

View on GitHub (pinned to 01a25a7d17)

Solutions

  1. Inspect the joined error(s) to see which closer failed; it is usually safe to restart vtadmin after fixing the underlying connection issue
  2. Ensure in-flight requests are drained before Close
  3. Check network/topo health if closers repeatedly fail
Defensive patterns

Strategy: try-catch

Try / catch

err := cluster.Close(ctx)
if err != nil {
    if strings.Contains(err.Error(), "failed to cleanly close cluster") {
        log.Warn("cluster shutdown had cleanup errors; inspecting",
            slog.Any("error", err), slog.String("cluster_id", cluster.ID))
        // non-fatal in most shutdown paths; proceed after logging
        return nil
    }
    return err
}

Prevention

When it happens

Trigger: 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.

Common situations: Shutdown while RPCs are in flight; already-broken connections erroring on Close; underlying servers returning errors during connection teardown.

Related errors


AI-assisted analysis of vitessio/vitess@01a25a7d17 (2026-09-01). Data as JSON: /api/errors/175db3e62b3a1fe4. Report an issue: GitHub.