vitessio/vitess · error

bad ping result: %v

Error message

bad ping result: %v

What it means

The gRPC tabletmanager client's Ping sends an RPC whose response is expected to echo the literal payload "payload". If the RPC succeeds but the returned Payload differs, the client assumes the response is wrong (wrong server, proxy mangling, corruption) and returns this error instead of silently treating the tablet as healthy.

Source

Thrown at go/vt/vttablet/grpctmclient/client.go:495

// ping runs the Ping RPC on an already-dialed connection, invalidating the pooled connection
// (when an invalidator is provided) on a connection-level failure.
func (client *Client) ping(ctx context.Context, c tabletmanagerservicepb.TabletManagerClient, invalidator invalidatorFunc) error {
	result, err := c.Ping(ctx, &tabletmanagerdatapb.PingRequest{
		Payload: "payload",
	})
	if err != nil {
		// Only invalidate (close + redial) the pooled connection when the failure indicates the
		// connection itself is broken. A DeadlineExceeded/Canceled means the RPC did not finish in
		// time, not that the conn is bad — invalidating then would close + redial the pool on a
		// momentarily slow but alive peer, adding churn exactly when it is already stressed.
		if invalidator != nil && shouldInvalidatePooledConn(err) {
			invalidator()
		}
		return vterrors.FromGRPC(err)
	}
	if result.Payload != "payload" {
		return fmt.Errorf("bad ping result: %v", result.Payload)
	}
	return nil
}

// shouldInvalidatePooledConn reports whether a failed pooled RPC indicates the connection itself is
// likely broken (so it should be closed and redialed) rather than a transient timeout or
// cancellation. A dead/unreachable peer surfaces as Unavailable and still invalidates.
func shouldInvalidatePooledConn(err error) bool {
	// status.Code only recognizes gRPC status errors: a raw or wrapped
	// context.Canceled / context.DeadlineExceeded (e.g. the monitor shutting
	// down or a caller-side timeout firing before the RPC hits the wire) maps
	// to codes.Unknown and would needlessly close and redial a healthy
	// connection. Check the context errors directly first.
	if errors.Is(err, context.Canceled) || errors.Is(err, context.DeadlineExceeded) {
		return false
	}
	switch status.Code(err) {
	case codes.DeadlineExceeded, codes.Canceled:

View on GitHub (pinned to 01a25a7d17)

Solutions

  1. Verify the tablet address points at the real vttablet grpc port (vtctldclient Ping the tablet directly)
  2. Check for proxies/load balancers between client and vttablet that might alter or empty the response
  3. Confirm vttablet and client are compatible versions; upgrade the mismatched component
  4. Enable gRPC logging on both sides to inspect the actual response payload
Defensive patterns

Strategy: try-catch

Validate before calling

// Verify the target speaks tabletmanager gRPC before relying on Ping
conn, err := grpc.Dial(addr); defer conn.Close()
_ = tabletpb.NewTabletManagerClient(conn)

Try / catch

if err := client.Ping(ctx, tabletAlias); err != nil {
    if strings.Contains(err.Error(), "bad ping result") {
        // suspect proxy/version mismatch; recheck endpoint
    }
}

Prevention

When it happens

Trigger: Ping or PingPooled on a gRPC tabletmanager client receives result.Payload != "payload" — e.g. an intermediary (proxy, older/mismatched vttablet build, different protocol) responds with an empty or altered body.

Common situations: Routing through an L7 proxy that rewrites gRPC bodies; a vttablet of a wildly different version answering the RPC; a misconfigured service pointing Ping at the wrong port/process.

Related errors


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