grpc/grpc-go · error

[xDS node id: %v]: %w

Error message

[xDS node id: %v]: %w

What it means

This is the annotateErrorWithNodeID() function which wraps any CDS balancer error with the xDS node ID from the bootstrap config. It is not an error condition itself but rather an error enrichment step. It uses %w verb to preserve the wrapped error for errors.Is/errors.As checking. The node ID helps identify which xDS client node encountered the error, critical for debugging multi-node deployments.

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 03255a9237)

Solutions

  1. Focus on the wrapped error (the part after '[xDS node id: ...]:') — this prefix is just context
  2. Verify the xDS node ID in your bootstrap config matches what you expect for your deployment
  3. If the node ID is empty or default, check your xDS bootstrap configuration file's node.id field
  4. Use errors.Unwrap() or errors.Is() to programmatically access the underlying error

Example fix

// before: bootstrap config with default/empty node ID
{"node": {"id": ""}}
// after: set a meaningful node ID
{"node": {"id": "my-service-instance-1"}}
Defensive patterns

Strategy: try-catch

Validate before calling

// Verify xDS bootstrap config has a valid node ID before starting
func validateBootstrapNodeID(bootstrapPath string) error {
    data, err := os.ReadFile(bootstrapPath)
    if err != nil {
        return err
    }
    var cfg struct{ Node struct{ Id string `json:"id"` } `json:"node"` }
    if err := json.Unmarshal(data, &cfg); err != nil {
        return err
    }
    if cfg.Node.Id == "" {
        return fmt.Errorf("xDS bootstrap node.id is empty")
    }
    return nil
}

Try / catch

// Use errors.Unwrap to access the underlying error
var underlying error
if inner := errors.Unwrap(err); inner != nil {
    underlying = inner
}
// The node ID prefix is context; the real error is the wrapped part
if errors.Is(underlying, xdsresource.ErrTypeResourceNotFound) {
    // handle resource-not-found scenario
}

Prevention

When it happens

Trigger: Called by handleXDSConfigUpdate() when a cluster is not found in the xDS config (for static clusters), and by handleClusterUpdate() for any error in outlier detection setup, LB policy unmarshalling, or child config pushing. The function retrieves the node ID from b.xdsClient.BootstrapConfig().Node().GetId() and wraps the original error.

Common situations: You see this prefix on any error from the CDS balancer — the actual error is the wrapped part after the node ID. The node ID comes from the xDS bootstrap configuration file. If the node ID is empty or wrong, it indicates a bootstrap configuration problem.

Related errors


AI-assisted analysis of grpc/grpc-go@03255a9237 (2026-08-07). Data as JSON: /api/errors/ae84745a6c5712c1. Report an issue: GitHub.