grpc/grpc-go · error
failed to unmarshal config
Error message
failed to unmarshal config: %v
What it means
Returned by tlscreds.NewBundle when json.Unmarshal of the mTLS channel-creds config fails. The expected shape is a JSON object with optional string fields certificate_file, ca_certificate_file, private_key_file, spiffe_trust_bundle_map_file.
Solutions
- Make the channel_creds config a JSON object with string-valued file fields.
- Use only the documented field names: certificate_file, private_key_file, ca_certificate_file, spiffe_trust_bundle_map_file.
- Validate the surrounding channel_creds entry shape {type, config}.
- Run the bootstrap through jq to catch syntax errors.
Example fix
// before
{"type":"tlscreds_mtls","config":["/etc/certs/client.crt"]}
// after
{"type":"tlscreds_mtls","config":{"certificate_file":"/etc/certs/client.crt","private_key_file":"/etc/certs/client.key","ca_certificate_file":"/etc/certs/ca.crt"}} Defensive patterns
Strategy: validation
Validate before calling
// Validate the tlscreds_mtls config object shape.
func validateTLSCredsConfig(raw json.RawMessage) error {
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"`
}
if err := json.Unmarshal(raw, &cfg); err != nil {
return fmt.Errorf("tlscreds config must be a JSON object: %w", err)
}
return nil
} Try / catch
if _, _, err := tlscreds.NewBundle(jd); err != nil {
if strings.Contains(err.Error(), "failed to unmarshal config") {
// reshape the channel_creds config to a JSON object.
}
} Prevention
- Make the tlscreds config a JSON object with string-valued file fields.
- Use only documented field names.
- Validate the whole bootstrap with jq before deploy.
When it happens
Trigger: Triggered at bundle.go:64 when json.Unmarshal(jd, cfg) errors. Typically the channel_creds config value is not a JSON object, or one of the file-path fields has a non-string type.
Common situations: config set to a bare string or array instead of an object; a path field set to a number/boolean; typo in a field name leaving a malformed value; trailing comma inside the config block.
Related errors
- failed to build credentials bundle from bootstrap for
- failed to unmarshal JWT call credentials config
- xds: error normalizing JSON bootstrap configuration
- xds: failed to JSON unmarshal server configurations during…
- xds: failed to JSON unmarshal server configuration during…
AI-assisted analysis of grpc/grpc-go@0c51461d27 (2026-08-11).
Data as JSON: /api/errors/10c210b21d48c364.
Report an issue: GitHub.
Appendix: 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 0c51461d27)