{"record":{"id":"10c210b21d48c364","repo":"grpc/grpc-go","slug":"failed-to-unmarshal-config-v","errorCode":null,"errorMessage":"failed to unmarshal config: %v","messagePattern":"failed to unmarshal config: (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/xds/bootstrap/tlscreds/bundle.go","lineNumber":64,"sourceCode":"}\n\n// NewBundle returns a credentials.Bundle which implements mTLS Credentials in xDS\n// Bootstrap File. It delegates certificate loading to a file_watcher provider\n// if either client certificates or server root CA is specified. The second\n// return value is a close func that should be called when the caller no longer\n// needs this bundle.\n// See gRFC A65: github.com/grpc/proposal/blob/master/A65-xds-mtls-creds-in-bootstrap.md\nfunc NewBundle(jd json.RawMessage) (credentials.Bundle, func(), error) {\n\tcfg := &struct {\n\t\tCertificateFile          string `json:\"certificate_file\"`\n\t\tCACertificateFile        string `json:\"ca_certificate_file\"`\n\t\tPrivateKeyFile           string `json:\"private_key_file\"`\n\t\tSPIFFETrustBundleMapFile string `json:\"spiffe_trust_bundle_map_file\"`\n\t}{}\n\n\tif jd != nil {\n\t\tif err := json.Unmarshal(jd, cfg); err != nil {\n\t\t\treturn nil, nil, fmt.Errorf(\"failed to unmarshal config: %v\", err)\n\t\t}\n\t} // Else the config field is absent. Treat it as an empty config.\n\n\tif !envconfig.XDSSPIFFEEnabled {\n\t\tcfg.SPIFFETrustBundleMapFile = \"\"\n\t}\n\tif cfg.CACertificateFile == \"\" && cfg.CertificateFile == \"\" && cfg.PrivateKeyFile == \"\" && cfg.SPIFFETrustBundleMapFile == \"\" {\n\t\t// We cannot use (and do not need) a file_watcher provider in this case,\n\t\t// and can simply directly use the TLS transport credentials.\n\t\t// Quoting A65:\n\t\t//\n\t\t// > The only difference between the file-watcher certificate provider\n\t\t// > config and this one is that in the file-watcher certificate\n\t\t// > provider, at least one of the \"certificate_file\" or\n\t\t// > \"ca_certificate_file\" fields must be specified, whereas in this\n\t\t// > configuration, it is acceptable to specify neither one.\n\t\t// Further, with the introduction of SPIFFE Trust Map support, we also\n\t\t// check for this value.","sourceCodeStart":46,"sourceCodeEnd":82,"githubUrl":"https://github.com/grpc/grpc-go/blob/0c51461d27177d997e14c642fe18c11668fc09a3/internal/xds/bootstrap/tlscreds/bundle.go#L46-L82","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\n{\"type\":\"tlscreds_mtls\",\"config\":[\"/etc/certs/client.crt\"]}\n\n// after\n{\"type\":\"tlscreds_mtls\",\"config\":{\"certificate_file\":\"/etc/certs/client.crt\",\"private_key_file\":\"/etc/certs/client.key\",\"ca_certificate_file\":\"/etc/certs/ca.crt\"}}","handlingStrategy":"validation","validationCode":"// Validate the tlscreds_mtls config object shape.\nfunc validateTLSCredsConfig(raw json.RawMessage) error {\n    var cfg struct {\n        CertificateFile          string `json:\"certificate_file\"`\n        CACertificateFile        string `json:\"ca_certificate_file\"`\n        PrivateKeyFile           string `json:\"private_key_file\"`\n        SPIFFETrustBundleMapFile string `json:\"spiffe_trust_bundle_map_file\"`\n    }\n    if err := json.Unmarshal(raw, &cfg); err != nil {\n        return fmt.Errorf(\"tlscreds config must be a JSON object: %w\", err)\n    }\n    return nil\n}","typeGuard":null,"tryCatchPattern":"if _, _, err := tlscreds.NewBundle(jd); err != nil {\n    if strings.Contains(err.Error(), \"failed to unmarshal config\") {\n        // reshape the channel_creds config to a JSON object.\n    }\n}","preventionTips":["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."],"tags":["grpc","xds","tls","channel-credentials","config","json","go"],"backgroundTag":null,"analyzedSha":"0c51461d27177d997e14c642fe18c11668fc09a3","analyzedAt":"2026-08-11T14:49:15.055Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}