grpc/grpc-go · error
error reading observability configuration file
Error message
error reading observability configuration file %q: %v
What it means
GRPC_GCP_OBSERVABILITY_CONFIG_FILE was set, but os.ReadFile failed on the referenced path. The %q is the file path and %v is the os error (most often 'no such file or directory' or 'permission denied'). This fires before any parsing.
Solutions
- Confirm the path printed in %q exists: `ls -l <path>` from the same user/namespace the process runs as.
- Fix permissions (e.g. `chmod 644 <path>`) or run the process under the owning uid.
- If running in Kubernetes, verify the ConfigMap/Secret volumeMount and that the container starts after the mount.
- Use an absolute path; do not rely on the process working directory.
- If the file is optional, unset GRPC_GCP_OBSERVABILITY_CONFIG_FILE (fall back to GRPC_GCP_OBSERVABILITY_CONFIG) or leave both unset.
Example fix
# before export GRPC_GCP_OBSERVABILITY_CONFIG_FILE=etc/obs.json # after export GRPC_GCP_OBSERVABILITY_CONFIG_FILE=/etc/grpc/observability.json
Defensive patterns
Strategy: validation
Validate before calling
if f := os.Getenv("GRPC_GCP_OBSERVABILITY_CONFIG_FILE"); f != "" {
info, err := os.Stat(f)
if err != nil { return fmt.Errorf("config file unreachable: %w", err) }
if info.IsDir() { return fmt.Errorf("config file is a directory: %s", f) }
if info.Mode().Perm()&0400 == 0 { return fmt.Errorf("config file not readable: %s", f) }
} Try / catch
if err := observability.Start(ctx); err != nil {
if strings.Contains(err.Error(), "error reading observability configuration file") {
log.Fatalf("mount/permission problem for GRPC_GCP_OBSERVABILITY_CONFIG_FILE: %v", err)
}
log.Fatalf("observability: %v", err)
} Prevention
- Use an absolute path for the config file env var.
- In Kubernetes, set the container startAfter the ConfigMap/Secret mount.
- chmod 644 the file or chown to the runtime uid.
When it happens
Trigger: Setting GRPC_GCP_OBSERVABILITY_CONFIG_FILE to a path that does not exist, is unreadable due to permissions, is a directory, or lives on a mount that is not attached in the container. observability.Start -> parseObservabilityConfig -> os.ReadFile.
Common situations: Container/pod deploy with a wrong mountPath; ConfigMap not mounted; file path relative to a different working directory; permission bits too strict (0600 owned by another uid); typo in the env var value; secret volume not yet attached at process start.
Related errors
- no ObservabilityConfig found
- cannot have a leading slash
- cannot have exclude and a '*' wildcard
- empty destination project ID
- error in clientRPCEvent method
AI-assisted analysis of grpc/grpc-go@0c51461d27 (2026-08-11).
Data as JSON: /api/errors/ae717ea33f73ef84.
Report an issue: GitHub.
Appendix: source
Thrown at gcp/observability/config.go:133
}
if err := validateLoggingEvents(&config); err != nil {
return nil, fmt.Errorf("error parsing observability config: %v", err)
}
if config.CloudTrace != nil && (config.CloudTrace.SamplingRate > 1 || config.CloudTrace.SamplingRate < 0) {
return nil, fmt.Errorf("error parsing observability config: invalid cloud trace sampling rate %v", config.CloudTrace.SamplingRate)
}
logger.Infof("Parsed ObservabilityConfig: %+v", &config)
return &config, nil
}
func parseObservabilityConfig() (*config, error) {
if f := envconfig.ObservabilityConfigFile; f != "" {
if envconfig.ObservabilityConfig != "" {
logger.Warning("Ignoring GRPC_GCP_OBSERVABILITY_CONFIG and using GRPC_GCP_OBSERVABILITY_CONFIG_FILE contents.")
}
content, err := os.ReadFile(f)
if err != nil {
return nil, fmt.Errorf("error reading observability configuration file %q: %v", f, err)
}
return unmarshalAndVerifyConfig(content)
} else if envconfig.ObservabilityConfig != "" {
return unmarshalAndVerifyConfig([]byte(envconfig.ObservabilityConfig))
}
// If the ENV var doesn't exist, do nothing
return nil, nil
}
func ensureProjectIDInObservabilityConfig(ctx context.Context, config *config) error {
if config.ProjectID == "" {
// Try to fetch the GCP project id
projectID := fetchDefaultProjectID(ctx)
if projectID == "" {
return fmt.Errorf("empty destination project ID")
}
config.ProjectID = projectID
}View on GitHub (pinned to 0c51461d27)