{"record":{"id":"7992f3bd9183f029","repo":"grpc/grpc-go","slug":"failed-to-unmarshal-jwt-call-credentials-config","errorCode":null,"errorMessage":"failed to unmarshal JWT call credentials config: %v","messagePattern":"failed to unmarshal JWT call credentials config: (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/xds/bootstrap/jwtcreds/call_creds.go","lineNumber":44,"sourceCode":"\t\"fmt\"\n\n\t\"google.golang.org/grpc/credentials\"\n\t\"google.golang.org/grpc/credentials/jwt\"\n)\n\n// NewCallCredentials returns a new JWT token based call credentials. The input\n// config must match the structure specified in gRFC A97.\n//\n// The caller is expected to invoke the cancel function when they are done using\n// the returned call creds. This cancel function is idempotent.\nfunc NewCallCredentials(configJSON json.RawMessage) (c credentials.PerRPCCredentials, cancel func(), err error) {\n\tvar cfg struct {\n\t\tJWTTokenFile string `json:\"jwt_token_file\"`\n\t}\n\temptyFn := func() {}\n\n\tif err := json.Unmarshal(configJSON, &cfg); err != nil {\n\t\treturn nil, emptyFn, fmt.Errorf(\"failed to unmarshal JWT call credentials config: %v\", err)\n\t}\n\tif cfg.JWTTokenFile == \"\" {\n\t\treturn nil, emptyFn, fmt.Errorf(\"jwt_token_file is required in JWT call credentials config\")\n\t}\n\tcallCreds, err := jwt.NewTokenFileCallCredentials(cfg.JWTTokenFile)\n\tif err != nil {\n\t\treturn nil, emptyFn, fmt.Errorf(\"failed to create JWT call credentials: %v\", err)\n\t}\n\treturn callCreds, emptyFn, nil\n}\n","sourceCodeStart":26,"sourceCodeEnd":55,"githubUrl":"https://github.com/grpc/grpc-go/blob/03255a9237b6eb32710f6bc4f2de9a675b99fe36/internal/xds/bootstrap/jwtcreds/call_creds.go#L26-L55","documentation":"jwtcreds.NewCallCredentials (call_creds.go:43-44) unmarshals the provided json.RawMessage into a struct expecting a single jwt_token_file string field. If the JSON is syntactically invalid or contains values of the wrong type, encoding/json fails and the error is wrapped here. This is the JWT call-credentials plugin for xDS bootstrap (gRFC A97).","triggerScenarios":"The bootstrap references a call_credentials block of type whose config blob is malformed JSON, or the config object has jwt_token_file set to a non-string value. Any json.Unmarshal failure on the config JSON triggers this.","commonSituations":"The call_creds config was templated with an unquoted variable; the config field was accidentally left as a JSON object instead of a string; a typo introduced invalid JSON syntax in the credentials sub-document.","solutions":["Validate that the call_credentials config value is a JSON object with a string jwt_token_file field.","Use jq or a JSON validator on the specific credentials sub-document.","Ensure templating engines quote file paths correctly when injecting them."],"exampleFix":"// before:\n//   { \"type\": \"jwt\", \"config\": { jwt_token_file: /etc/tokens/jwt } }\n// after:\n//   { \"type\": \"jwt\", \"config\": { \"jwt_token_file\": \"/etc/tokens/jwt\" } }","handlingStrategy":"validation","validationCode":"// Validate the JWT creds config JSON before passing to NewCallCredentials.\nfunc validateJWTCredsConfig(cfg json.RawMessage) error {\n    var probe struct{ JWTTokenFile string `json:\"jwt_token_file\"` }\n    if err := json.Unmarshal(cfg, &probe); err != nil {\n        return fmt.Errorf(\"jwt creds config not valid JSON: %w\", err)\n    }\n    return nil\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Ensure all string values in the credentials config are quoted.","Validate credentials sub-documents with a JSON linter as part of bootstrap validation."],"tags":["xds","bootstrap","jwt","credentials","json","grpc"],"analyzedSha":"03255a9237b6eb32710f6bc4f2de9a675b99fe36","analyzedAt":"2026-08-07T00:29:34.215Z","schemaVersion":2},"datasetVersion":"2026-08-07T03:17:09.362Z"}