grpc/grpc-go · error

failed to unmarshal config: %v

Error message

failed to unmarshal config: %v

What it means

tlscreds.NewBundle (bundle.go:62-64) unmarshals the provided json.RawMessage into a struct expecting certificate_file, ca_certificate_file, private_key_file, and spiffe_trust_bundle_map_file string fields. If the JSON is invalid or a field has the wrong value type, encoding/json fails and the error is wrapped here.

Source

Thrown at internal/xds/bootstrap/tlscreds/bundle.go:64

}

// NewBundle returns a credentials.Bundle which implements mTLS Credentials in xDS
// Bootstrap File. It delegates certificate loading to a file_watcher provider
// if either client certificates or server root CA is specified. The second
// return value is a close func that should be called when the caller no longer
// needs this bundle.
// See gRFC A65: github.com/grpc/proposal/blob/master/A65-xds-mtls-creds-in-bootstrap.md
func NewBundle(jd json.RawMessage) (credentials.Bundle, func(), error) {
	cfg := &struct {
		CertificateFile          string `json:"certificate_file"`
		CACertificateFile        string `json:"ca_certificate_file"`
		PrivateKeyFile           string `json:"private_key_file"`
		SPIFFETrustBundleMapFile string `json:"spiffe_trust_bundle_map_file"`
	}{}

	if jd != nil {
		if err := json.Unmarshal(jd, cfg); err != nil {
			return nil, nil, fmt.Errorf("failed to unmarshal config: %v", err)
		}
	} // Else the config field is absent. Treat it as an empty config.

	if !envconfig.XDSSPIFFEEnabled {
		cfg.SPIFFETrustBundleMapFile = ""
	}
	if cfg.CACertificateFile == "" && cfg.CertificateFile == "" && cfg.PrivateKeyFile == "" && cfg.SPIFFETrustBundleMapFile == "" {
		// We cannot use (and do not need) a file_watcher provider in this case,
		// and can simply directly use the TLS transport credentials.
		// Quoting A65:
		//
		// > The only difference between the file-watcher certificate provider
		// > config and this one is that in the file-watcher certificate
		// > provider, at least one of the "certificate_file" or
		// > "ca_certificate_file" fields must be specified, whereas in this
		// > configuration, it is acceptable to specify neither one.
		// Further, with the introduction of SPIFFE Trust Map support, we also
		// check for this value.

View on GitHub (pinned to 03255a9237)

Solutions

  1. Validate that the credentials config is a JSON object with only string-valued fields (certificate_file, ca_certificate_file, private_key_file, spiffe_trust_bundle_map_file).
  2. Run the config sub-document through a JSON validator.
  3. Ensure all file-path values are quoted strings.

Example fix

// before:
//   { "certificate_file": /etc/certs/client.pem }
// after:
//   { "certificate_file": "/etc/certs/client.pem",
//     "private_key_file": "/etc/certs/client.key",
//     "ca_certificate_file": "/etc/certs/ca.pem" }
Defensive patterns

Strategy: validation

Validate before calling

// Validate the TLS creds config JSON structure.
func validateTLSCredsConfig(jd json.RawMessage) error {
    if jd == nil { return nil }
    var cfg struct {
        CertificateFile          *string `json:"certificate_file"`
        CACertificateFile        *string `json:"ca_certificate_file"`
        PrivateKeyFile           *string `json:"private_key_file"`
        SPIFFETrustBundleMapFile *string `json:"spiffe_trust_bundle_map_file"`
    }
    return json.Unmarshal(jd, &cfg)
}

Prevention

When it happens

Trigger: The xDS TLS credentials config block (mTLS in bootstrap, gRFC A65) is malformed JSON, or one of its fields is a number/object instead of a string. Note: a nil config is allowed and treated as empty, so this only fires when jd is non-nil but invalid.

Common situations: The mTLS config was hand-edited and a path was left unquoted; a templating system injected a structured value where a string was expected; the config was copied from a YAML source without conversion.

Related errors


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