grpc/grpc-go · info
[xDS node id: ]
Error message
[xDS node id: %v]: %w
What it means
This is the error format string used by annotateErrorWithNodeID (line 443) to wrap xDS-related errors with the bootstrap node ID for diagnostics. The %w verb wraps the original error (preserving errors.Is/As chains) and %v inserts the node ID from b.xdsClient.BootstrapConfig().Node().GetId(). It is not an error itself but a diagnostic prefix applied to errors 345, 346, 347, and any error from handleClusterUpdate via line 259. The node ID helps correlate client-side errors with xDS server logs.
Solutions
- Look past the '[xDS node id: ...]:' prefix to find the actual underlying error and remediate that specific issue.
- Use the node ID to correlate with xDS management server logs — search for this node ID in the server's request/response logs.
- Verify the node ID in your bootstrap configuration matches expectations (it should be unique per client instance).
- If using errors.Is/As to programmatically detect the inner error, note that %w preserves the wrapping chain so these still work.
Defensive patterns
Strategy: try-catch
Try / catch
// This is an error wrapper, not an error type. The underlying error
// is preserved via %w. Use errors.Is/As to detect the inner error:
var clusterNotFoundErr error // define sentinel if needed
if errors.Is(err, clusterNotFoundErr) {
// handle missing cluster
}
// Extract node ID from the message for correlation:
if strings.HasPrefix(err.Error(), "[xDS node id:") {
parts := strings.SplitN(err.Error(), "]:", 2)
nodeID := strings.TrimPrefix(parts[0], "[xDS node id: ")
log.Printf("xDS error from node %s: %s", nodeID, parts[1])
} Prevention
- Always look past the '[xDS node id:]' prefix to find the root cause.
- Use the node ID to correlate with xDS server-side logs.
- Verify the bootstrap config's node ID is unique and identifiable per deployment.
When it happens
Trigger: Any error path in handleXDSConfigUpdate or handleClusterUpdate that calls annotateErrorWithNodeID: missing cluster (345), outlier detection failure (346), LB policy unmarshal failure (347), or child config update failure (line 259). The wrapper adds '[xDS node id: <id>]:' prefix to the original error message.
Common situations: This prefix appears whenever a CDS balancer encounters a configuration error tied to a specific cluster resource. The node ID value is critical for debugging — it identifies which xDS client (from the bootstrap config) generated the error, useful in multi-tenant or multi-server setups.
Related errors
- did not find the cluster
- error unmarshalling xDS LB Policy
- failed to build child policy config
- failed to correctly update Outlier Detection config
- security configuration on the client-side does not contain…
AI-assisted analysis of grpc/grpc-go@0c51461d27 (2026-08-11).
Data as JSON: /api/errors/ae84745a6c5712c1.
Report an issue: GitHub.
Appendix: source
Thrown at internal/xds/balancer/cdsbalancer/cdsbalancer.go:443
// ExitIdle (but still checks for the interface's existence to
// avoid a panic if not). If the child does not, no subconns
// will be connected.
b.childLB.ExitIdle()
}
// Node ID needs to be manually added to errors generated in the following
// scenarios:
// - resource-does-not-exist: since the xDS watch API uses a separate callback
// instead of returning an error value. TODO(gRFC A88): Once A88 is
// implemented, the xDS client will be able to add the node ID to
// resource-does-not-exist errors as well, and we can get rid of this
// special handling.
// - received a good update from the xDS client, but the update either contains
// an invalid security configuration or contains invalid aggragate cluster
// config.
func (b *cdsBalancer) annotateErrorWithNodeID(err error) error {
nodeID := b.xdsClient.BootstrapConfig().Node().GetId()
return fmt.Errorf("[xDS node id: %v]: %w", nodeID, err)
}
// onClusterAmbientError handles an ambient error, if a childLB already has a
// good update, it should continue using that.
func (b *cdsBalancer) onClusterAmbientError(name string, err error) {
b.logger.Warningf("Cluster resource %q received ambient error update: %v", name, err)
if xdsresource.ErrType(err) != xdsresource.ErrorTypeConnection && b.childLB != nil {
// Connection errors will be sent to the child balancers directly.
// There's no need to forward them.
b.childLB.ResolverError(err)
}
}
// onClusterResourceError handles errors to stop using the previously seen
// resource. Propagates the error down to the child policy if one exists, and
// puts the channel in TRANSIENT_FAILURE.
func (b *cdsBalancer) onClusterResourceError(name string, err error) {View on GitHub (pinned to 0c51461d27)