hasura/graphql-engine · error · errors.Error

cannot read global config from file/env: %w

Error message

cannot read global config from file/env: %w

What it means

readGlobalConfig uses viper to read config.yaml in ec.GlobalConfigDir plus environment overrides, and v.ReadInConfig() failed. Unlike 215, this covers viper-level failures: unreadable file (permissions on an existing config), unsupported/ambiguous config format for the discovered file, or env-var binding issues; a genuinely absent file is normally handled as NotFound elsewhere.

Source

Thrown at cli/global_config.go:227

	return nil
}

// readGlobalConfig reads the configuration from global config file env vars,
// through viper.
func (ec *ExecutionContext) readGlobalConfig() error {
	var op errors.Op = "cli.ExecutionContext.readGlobalConfig"
	// need to get existing viper because https://github.com/spf13/viper/issues/233
	v := viper.New()
	v.SetEnvPrefix("HASURA_GRAPHQL")
	v.AutomaticEnv()
	v.SetConfigName("config")
	v.AddConfigPath(ec.GlobalConfigDir)
	v.SetDefault("cli_environment", DefaultEnvironment)

	err := v.ReadInConfig()
	if err != nil {
		return errors.E(op, fmt.Errorf("cannot read global config from file/env: %w", err))
	}

	if ec.GlobalConfig == nil {
		ec.Logger.Debugf("global config is not pre-set, reading from current env")
		ec.GlobalConfig = &GlobalConfig{
			UUID:                   v.GetString("uuid"),
			EnableTelemetry:        v.GetBool("enable_telemetry"),
			ShowUpdateNotification: v.GetBool("show_update_notification"),
			CLIEnvironment:         Environment(v.GetString("cli_environment")),
		}
	} else {
		ec.Logger.Debugf("global config is pre-set to %#v", ec.GlobalConfig)
	}

	ec.Logger.Debugf("global config: uuid: %v", ec.GlobalConfig.UUID)
	ec.Logger.Debugf("global config: enableTelemetry: %v", ec.GlobalConfig.EnableTelemetry)
	ec.Logger.Debugf(
		"global config: showUpdateNotification: %v",

View on GitHub (pinned to 724551b9ae)

Solutions

  1. Inspect the wrapped viper error: ConfigFileNotFoundError means path issues, *fs.PathError means permissions, IncompatibleConfigError means wrong types
  2. Fix permissions/ownership of GlobalConfigDir/config.* so the running user can read it
  3. Rename the config file to config.yaml (the name SetConfigName("config") expects) and ensure a supported extension
  4. Sanitize environment variables that override config values to valid types

Example fix

# before
-rw------- 1 root root /root/.mycli/config.yaml
# after
$ sudo chmod 644 /root/.mycli/config.yaml
Defensive patterns

Strategy: validation

Validate before calling

p := filepath.Join(ec.GlobalConfigDir, "config.yaml")
if _, err := os.Stat(p); err == nil {
    if f, err := os.Open(p); err != nil {
        return fmt.Errorf("config exists but is unreadable: %w", err)
    }
    _ = f.Close()
}

Type guard

func configReadableAndNamed(dir string) bool {
    p := filepath.Join(dir, "config.yaml")
    f, err := os.Open(p)
    if err != nil {
        return false
    }
    _ = f.Close()
    return true
}

Try / catch

if err := ec.Prepare(ctx); err != nil {
    if strings.Contains(err.Error(), "cannot read global config from file/env") {
        var pathErr *fs.PathError
        if stderrors.As(err, &pathErr) {
            _ = os.Chmod(ec.GlobalConfigDir+"/config.yaml", 0o644)
            return ec.Prepare(ctx)
        }
    }
    return err
}

Prevention

When it happens

Trigger: GlobalConfigDir contains a config file that exists but cannot be opened (mode 000, ownership mismatch), has an extension viper cannot infer a format from, or global config was pre-set to nil while env vars referenced by SetDefault/bind fail to coerce.

Common situations: Root-created config files read by unprivileged users, a config file with an odd extension, stale viper cache of a deleted file, or env vars (CLI_ENVIRONMENT etc.) set to values that fail type coercion into config fields.

Related errors


AI-assisted analysis of hasura/graphql-engine@724551b9ae (2026-08-28). Data as JSON: /api/errors/8fad8d281440c0ae. Report an issue: GitHub.