{"record":{"id":"9af7234d98c9b919","repo":"grpc/grpc-go","slug":"credentials-failed-to-read-the-service-account-ke","errorCode":null,"errorMessage":"credentials: failed to read the service account key file: %v","messagePattern":"credentials: failed to read the service account key file: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"credentials/oauth/oauth.go","lineNumber":78,"sourceCode":"// removeServiceNameFromJWTURI removes RPC service name from URI.\nfunc removeServiceNameFromJWTURI(uri string) (string, error) {\n\tparsed, err := url.Parse(uri)\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\tparsed.Path = \"/\"\n\treturn parsed.String(), nil\n}\n\ntype jwtAccess struct {\n\tjsonKey []byte\n}\n\n// NewJWTAccessFromFile creates PerRPCCredentials from the given keyFile.\nfunc NewJWTAccessFromFile(keyFile string) (credentials.PerRPCCredentials, error) {\n\tjsonKey, err := os.ReadFile(keyFile)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"credentials: failed to read the service account key file: %v\", err)\n\t}\n\treturn NewJWTAccessFromKey(jsonKey)\n}\n\n// NewJWTAccessFromKey creates PerRPCCredentials from the given jsonKey.\nfunc NewJWTAccessFromKey(jsonKey []byte) (credentials.PerRPCCredentials, error) {\n\treturn jwtAccess{jsonKey}, nil\n}\n\nfunc (j jwtAccess) GetRequestMetadata(ctx context.Context, uri ...string) (map[string]string, error) {\n\t// Remove RPC service name from URI that will be used as audience\n\t// in a self-signed JWT token. It follows https://google.aip.dev/auth/4111.\n\taud, err := removeServiceNameFromJWTURI(uri[0])\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\t// TODO: the returned TokenSource is reusable. Store it in a sync.Map, with\n\t// uri as the key, to avoid recreating for every RPC.","sourceCodeStart":60,"sourceCodeEnd":96,"githubUrl":"https://github.com/grpc/grpc-go/blob/0c51461d27177d997e14c642fe18c11668fc09a3/credentials/oauth/oauth.go#L60-L96","documentation":"Returned by NewJWTAccessFromFile when os.ReadFile cannot read the service-account JSON key file. The %v is the OS error. This happens at credential construction time (not per-RPC), so the failure surfaces immediately when building the gRPC channel. NewServiceAccountFromFile wraps the same root cause with an identical message.","triggerScenarios":"Passing a non-existent or unreadable key file path to oauth.NewJWTAccessFromFile or oauth.NewServiceAccountFromFile; relative path resolved against the wrong working directory; missing file permission.","commonSituations":"Deploying without mounting the service-account key Secret; path typo; running as a user without read permission; CI using a different key path than production.","solutions":["Confirm the key file path exists and is readable: ls -l /path/to/key.json.","Use an absolute path resolved from a single config source.","Mount the Kubernetes Secret/ConfigMap containing the key before the process starts.","Prefer Application Default Credentials (GOOGLE_APPLICATION_CREDENTIALS or metadata server) over manual file paths when possible."],"exampleFix":"// before\ncreds, err := oauth.NewJWTAccessFromFile(\"service-account.json\")\n// after\nkeyPath := \"/etc/secrets/gcp/service-account.json\"\nif _, err := os.Stat(keyPath); err != nil {\n    log.Fatalf(\"key file missing: %v\", err)\n}\ncreds, err := oauth.NewJWTAccessFromFile(keyPath)","handlingStrategy":"validation","validationCode":"func loadKey(path string) ([]byte, error) {\n    if _, err := os.Stat(path); err != nil {\n        return nil, fmt.Errorf(\"service-account key missing at %q: %w\", path, err)\n    }\n    return os.ReadFile(path)\n}\n\njsonKey, err := loadKey(keyPath)\nif err != nil {\n    log.Fatal(err)\n}\ncreds, err := oauth.NewJWTAccessFromKey(jsonKey)","typeGuard":null,"tryCatchPattern":"creds, err := oauth.NewJWTAccessFromFile(keyPath)\nif err != nil {\n    return fmt.Errorf(\"cannot load service-account key %q: %w\", keyPath, err)\n}","preventionTips":["Stat the key file at startup and fail with a clear message.","Prefer GOOGLE_APPLICATION_CREDENTIALS / ADC over hardcoded paths.","Use absolute paths and verify the Kubernetes Secret is mounted."],"tags":["grpc","oauth","filesystem","service-account","credentials"],"backgroundTag":null,"analyzedSha":"0c51461d27177d997e14c642fe18c11668fc09a3","analyzedAt":"2026-08-11T14:49:15.055Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}