thanos-io/thanos · error
target not available: failed to dial peer
Error message
target not available: failed to dial peer: %v
What it means
This is the fully-wrapped error returned by peerGroup.getConnection when a Protobuf replication dial fails: the dial error is wrapped as "failed to dial peer" and then again with the errUnavailable prefix, yielding "target not available: failed to dial peer: <cause>". It tells callers of the receive handler that the target peer could not be reached, so the write could not be forwarded for replication. The failing endpoint is simultaneously marked unavailable so later calls fail fast with errUnavailable until a backoff re-allow.
Solutions
- Check the root cause in the inner error (%v suffix) — usually connection refused/timeout — and fix the peer's availability first.
- Validate and correct hashring endpoint addresses/ports in --receive.hashrings.
- Confirm Kubernetes Services/NetworkPolicies allow the receiver to reach peer gRPC ports.
- Align gRPC/TLS client dial options with the peer's server TLS configuration.
- Retry the write; replication failures can be retried once the peer's backoff state recovers.
Example fix
// caller side: don't hard-fail one unreplicable peer, log and continue
if err != nil {
level.Error(logger).Log("msg", "failed to forward to peer", "err", err)
return // or retry with backoff
} Defensive patterns
Strategy: retry
Validate before calling
// Go: check inner cause before retrying
if err != nil {
var netErr net.Error
if errors.As(err, &netErr) && netErr.Timeout() {
// retryable
}
} Try / catch
// Go: inspect the wrapped chain
if err != nil {
if strings.Contains(err.Error(), "target not available") {
// peer marked unavailable — schedule retry with backoff
}
} Prevention
- Use retries with jittered backoff around receive writes.
- Keep hashring config in sync across all receiver instances (configmaps/helm).
- Alert on replication failure metrics instead of silently dropping writes.
- Ensure graceful shutdown drains replication writes before peers go away.
When it happens
Trigger: Produced only in the ProtobufReplication branch of getConnection: p.dialer(endpoint.Address, p.dialOpts...) returns an error, causing return nil, errors.Wrap(dialError, errUnavailable.Error()). Callers hitting this include the receive Handler's forwarding path during replication to a remote endpoint.
Common situations: Rolling restarts or node failures take a peer down mid-write; misconfigured hashring addresses; NetworkPolicies dropping gRPC traffic; the peer's TCP port not exposed by the service; TLS handshake mismatch between receiver dialer options and peer server options.
Understand the failure class
Background: ECONNREFUSED and "connection refused" / "could not connect to server" errors: what they mean and how to fix them — this error's family across 44 libraries.
Related errors
- forwarding request to endpoint
- failed to dial peer
- no query API server reachable
- failed to get tsdb status from prometheus
- retrieving targets
AI-assisted analysis of thanos-io/thanos@35b8b99117 (2026-09-07).
Data as JSON: /api/errors/af3e67f641a5139e.
Report an issue: GitHub.
Appendix: source
Thrown at pkg/receive/handler.go:1982
p.conns.Inc()
var client peerClient
if isLocalEndpoint(endpoint, p.localEndpoint) {
client = &localAsyncWriter{
w: p.writer,
}
} else {
switch p.replicationProtocol {
case CapNProtoReplication:
client = writecapnp.NewRemoteWriteClient(writecapnp.NewTCPDialer(endpoint.CapNProtoAddress), p.logger)
case ProtobufReplication:
conn, err := p.dialer(endpoint.Address, p.dialOpts...)
if err != nil {
p.markPeerUnavailableUnlocked(endpoint)
dialError := errors.Wrap(err, "failed to dial peer")
return nil, errors.Wrap(dialError, errUnavailable.Error())
}
client = newProtobufPeer(conn)
default:
return nil, errors.Errorf("unknown replication protocol %v", p.replicationProtocol)
}
}
var delay time.Duration
if p.conns.Load() == 2 {
delay = p.maxArtificialDelay
}
p.connections[endpoint] = newPeerWorker(client, p.forwardDelay.WithLabelValues(endpoint.Address), p.asyncForwardWorkersCount, delay)
return p.connections[endpoint], nil
}
func (p *peerGroup) markPeerUnavailable(addr Endpoint) {
p.m.Lock()View on GitHub (pinned to 35b8b99117)