{"record":{"id":"f1a0f53a45f1b611","repo":"grpc/grpc-go","slug":"empty-token-exchange-service-uri-in-options","errorCode":null,"errorMessage":"empty token_exchange_service_uri in options","messagePattern":"empty token_exchange_service_uri in options","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"credentials/sts/sts.go","lineNumber":213,"sourceCode":"\nfunc makeHTTPClient(roots *x509.CertPool) httpDoer {\n\treturn &http.Client{\n\t\tTimeout: stsRequestTimeout,\n\t\tTransport: &http.Transport{\n\t\t\tTLSClientConfig: &tls.Config{\n\t\t\t\tRootCAs: roots,\n\t\t\t},\n\t\t},\n\t}\n}\n\n// validateOptions performs the following validation checks on opts:\n// - tokenExchangeServiceURI is not empty\n// - tokenExchangeServiceURI is a valid URI with a http(s) scheme\n// - subjectTokenPath and subjectTokenType are not empty.\nfunc validateOptions(opts Options) error {\n\tif opts.TokenExchangeServiceURI == \"\" {\n\t\treturn errors.New(\"empty token_exchange_service_uri in options\")\n\t}\n\tu, err := url.Parse(opts.TokenExchangeServiceURI)\n\tif err != nil {\n\t\treturn err\n\t}\n\tif u.Scheme != \"http\" && u.Scheme != \"https\" {\n\t\treturn fmt.Errorf(\"scheme is not supported: %q. Only http(s) is supported\", u.Scheme)\n\t}\n\n\tif opts.SubjectTokenPath == \"\" {\n\t\treturn errors.New(\"required field SubjectTokenPath is not specified\")\n\t}\n\tif opts.SubjectTokenType == \"\" {\n\t\treturn errors.New(\"required field SubjectTokenType is not specified\")\n\t}\n\treturn nil\n}\n","sourceCodeStart":195,"sourceCodeEnd":231,"githubUrl":"https://github.com/grpc/grpc-go/blob/03255a9237b6eb32710f6bc4f2de9a675b99fe36/credentials/sts/sts.go#L195-L231","documentation":"Returned by sts.validateOptions (credentials/sts/sts.go:213) when opts.TokenExchangeServiceURI is the empty string. NewCredentials calls validateOptions before building the STS call-credentials instance, so this is a hard constructor failure: the STS plugin (RFC 8693 token exchange) cannot know which server to POST the token-exchange request to without a URI, so it refuses to construct.","triggerScenarios":"Calling sts.NewCredentials(Options{...}) without setting TokenExchangeServiceURI, or with it explicitly empty. Any subsequent code that ignores the returned error and tries to use the nil credentials will then panic.","commonSituations":"Reading the STS Options from a JSON/env config and the field is absent or misnamed; copy-pasting a sample that omits the field; refactoring that renamed the struct field; config templating that renders an empty value.","solutions":["Set Options.TokenExchangeServiceURI to the STS endpoint URL before calling NewCredentials.","Always check the error returned by sts.NewCredentials and fail fast if non-nil.","Validate the config struct in your own config-loading layer (non-empty + http(s) scheme) before passing it to NewCredentials."],"exampleFix":"// before\nc, err := sts.NewCredentials(sts.Options{\n    SubjectTokenPath: \"/var/run/secrets/token\",\n    SubjectTokenType: \"urn:ietf:params:oauth:token-type:jwt\",\n}) // err: empty token_exchange_service_uri\n\n// after\nc, err := sts.NewCredentials(sts.Options{\n    TokenExchangeServiceURI: \"https://sts.googleapis.com/v1/token\",\n    SubjectTokenPath:        \"/var/run/secrets/token\",\n    SubjectTokenType:       \"urn:ietf:params:oauth:token-type:jwt\",\n})","handlingStrategy":"validation","validationCode":"// Validate STS Options before constructing credentials.\nfunc validateSTS(o sts.Options) error {\n    if o.TokenExchangeServiceURI == \"\" {\n        return errors.New(\"TokenExchangeServiceURI is required\")\n    }\n    if u, err := url.Parse(o.TokenExchangeServiceURI); err != nil || (u.Scheme != \"http\" && u.Scheme != \"https\") {\n        return fmt.Errorf(\"TokenExchangeServiceURI must be http(s), got %q\", o.TokenExchangeServiceURI)\n    }\n    if o.SubjectTokenPath == \"\" {\n        return errors.New(\"SubjectTokenPath is required\")\n    }\n    if o.SubjectTokenType == \"\" {\n        return errors.New(\"SubjectTokenType is required\")\n    }\n    return nil\n}\n\nif err := validateSTS(opts); err != nil { log.Fatal(err) }\nc, err := sts.NewCredentials(opts)","typeGuard":null,"tryCatchPattern":"c, err := sts.NewCredentials(opts)\nif err != nil {\n    // err contains the exact missing-field message; log and abort startup\n    log.Fatalf(\"STS credentials invalid: %v\", err)\n}","preventionTips":["Load STS config into a typed struct and run a single validate() at startup.","Always check the error from sts.NewCredentials; never assume success.","Unit-test your config parser against a golden Options that passes validateOptions."],"tags":["go","grpc","security","sts","credentials","config-validation"],"analyzedSha":"03255a9237b6eb32710f6bc4f2de9a675b99fe36","analyzedAt":"2026-08-07T00:29:34.215Z","schemaVersion":2},"datasetVersion":"2026-08-07T03:17:09.362Z"}