grpc/grpc-go · error

error reading observability configuration file %q: %v

Error message

error reading observability configuration file %q: %v

What it means

parseObservabilityConfig reads the file at GRPC_GCP_OBSERVABILITY_CONFIG_FILE and os.ReadFile returned an error (the message includes the path and the OS error). This fires before any JSON parsing, so the file is missing, unreadable, or the path is wrong. When this env var is set, it takes precedence over GRPC_GCP_OBSERVABILITY_CONFIG.

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 03255a9237)

Solutions

  1. Verify the path exists and is readable by the process: `ls -l <path>` and `cat <path>` from inside the container.
  2. Use an absolute path for the env var; ensure ConfigMap/Secret mounts land at that exact path.
  3. If you meant to use the inline env var instead, unset GRPC_GCP_OBSERVABILITY_CONFIG_FILE and set GRPC_GCP_OBSERVABILITY_CONFIG.

Example fix

# before
export GRPC_GCP_OBSERVABILITY_CONFIG_FILE=/etc/grpc/obs.yaml
# (file is YAML, missing, or wrong path)

# after
export GRPC_GCP_OBSERVABILITY_CONFIG_FILE=/etc/grpc/obs.json
# ensure /etc/grpc/obs.json exists, is JSON, and is world-readable
Defensive patterns

Strategy: validation

Validate before calling

func readableObservabilityConfigFile(path string) error {
    info, err := os.Stat(path)
    if err != nil { return err }
    if info.IsDir() { return errors.New("path is a directory") }
    if info.Mode().Perm()&0400 == 0 { return errors.New("file not readable") }
    return nil
}

Try / catch

content, err := os.ReadFile(path)
if err != nil { return fmt.Errorf("read obs config %s: %w", path, err) }

Prevention

When it happens

Trigger: GRPC_GCP_OBSERVABILITY_CONFIG_FILE points to a path that does not exist, is a directory, or has restrictive permissions; the env var was set to a relative path but the process's working directory differs.

Common situations: Kubernetes ConfigMap mount path mistyped; file mounted read-only to a different UID; relative path used in a container whose CWD is /; dangling symlink.

Related errors


AI-assisted analysis of grpc/grpc-go@03255a9237 (2026-08-07). Data as JSON: /api/errors/ae717ea33f73ef84. Report an issue: GitHub.