grpc/grpc-go · error
credentials: failed to read the service account key file
Error message
credentials: failed to read the service account key file: %v
What it means
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.
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.
Example fix
// before
creds, err := oauth.NewJWTAccessFromFile("service-account.json")
// after
keyPath := "/etc/secrets/gcp/service-account.json"
if _, err := os.Stat(keyPath); err != nil {
log.Fatalf("key file missing: %v", err)
}
creds, err := oauth.NewJWTAccessFromFile(keyPath) Defensive patterns
Strategy: validation
Validate before calling
func loadKey(path string) ([]byte, error) {
if _, err := os.Stat(path); err != nil {
return nil, fmt.Errorf("service-account key missing at %q: %w", path, err)
}
return os.ReadFile(path)
}
jsonKey, err := loadKey(keyPath)
if err != nil {
log.Fatal(err)
}
creds, err := oauth.NewJWTAccessFromKey(jsonKey) Try / catch
creds, err := oauth.NewJWTAccessFromFile(keyPath)
if err != nil {
return fmt.Errorf("cannot load service-account key %q: %w", keyPath, err)
} Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- unable to transfer serviceAccount PerRPCCredentials
- token file access error
- token file is empty
- unable to transfer jwtAccess PerRPCCredentials
- unable to transfer oauthAccess PerRPCCredentials
AI-assisted analysis of grpc/grpc-go@0c51461d27 (2026-08-11).
Data as JSON: /api/errors/9af7234d98c9b919.
Report an issue: GitHub.
Appendix: source
Thrown at credentials/oauth/oauth.go:78
// removeServiceNameFromJWTURI removes RPC service name from URI.
func removeServiceNameFromJWTURI(uri string) (string, error) {
parsed, err := url.Parse(uri)
if err != nil {
return "", err
}
parsed.Path = "/"
return parsed.String(), nil
}
type jwtAccess struct {
jsonKey []byte
}
// NewJWTAccessFromFile creates PerRPCCredentials from the given keyFile.
func NewJWTAccessFromFile(keyFile string) (credentials.PerRPCCredentials, error) {
jsonKey, err := os.ReadFile(keyFile)
if err != nil {
return nil, fmt.Errorf("credentials: failed to read the service account key file: %v", err)
}
return NewJWTAccessFromKey(jsonKey)
}
// NewJWTAccessFromKey creates PerRPCCredentials from the given jsonKey.
func NewJWTAccessFromKey(jsonKey []byte) (credentials.PerRPCCredentials, error) {
return jwtAccess{jsonKey}, nil
}
func (j jwtAccess) GetRequestMetadata(ctx context.Context, uri ...string) (map[string]string, error) {
// Remove RPC service name from URI that will be used as audience
// in a self-signed JWT token. It follows https://google.aip.dev/auth/4111.
aud, err := removeServiceNameFromJWTURI(uri[0])
if err != nil {
return nil, err
}
// TODO: the returned TokenSource is reusable. Store it in a sync.Map, with
// uri as the key, to avoid recreating for every RPC.View on GitHub (pinned to 0c51461d27)