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
- Verify the tablet address points at the real vttablet grpc port (vtctldclient Ping the tablet directly)
- Check for proxies/load balancers between client and vttablet that might alter or empty the response
- Confirm vttablet and client are compatible versions; upgrade the mismatched component
- 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
- Ping tablets directly without intermediary proxies first
- Keep vttablet and client versions aligned
- Verify port mappings point at vttablet grpc, not http or mysql ports
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
- PrimaryPosition(%s) failed: %w
- ReplicationStatus(%s) failed: %s
- cannot connect to tablet %v: %v
- BeforeSchema differs
- AfterSchema differs
AI-assisted analysis of vitessio/vitess@01a25a7d17 (2026-09-01).
Data as JSON: /api/errors/68e2badd7e36285d.
Report an issue: GitHub.