cloudflare/cloudflared · error

unable to find config file

Error message

unable to find config file

What it means

readConfigFromPath loads and parses the cloudflared Root configuration from a given path. If it is handed an empty string, there is no file to open, so it immediately returns 'unable to find config file'. This guards the fast path before os.Open produces its own error.

Source

Thrown at config/manager.go:74

	notifier.ConfigDidUpdate(config)

	m.watcher.Start(m)
	return nil
}

// GetConfig reads the yaml file from the disk
func (m *FileManager) GetConfig() (Root, error) {
	return m.ReadConfig(m.configPath, m.log)
}

// Shutdown stops the watcher
func (m *FileManager) Shutdown() {
	m.watcher.Shutdown()
}

func readConfigFromPath(configPath string, log *zerolog.Logger) (Root, error) {
	if configPath == "" {
		return Root{}, errors.New("unable to find config file")
	}

	file, err := os.Open(configPath)
	if err != nil {
		return Root{}, err
	}
	defer file.Close()

	var config Root
	if err := yaml.NewDecoder(file).Decode(&config); err != nil {
		if err == io.EOF {
			log.Error().Msgf("Configuration file %s was empty", configPath)
			return Root{}, nil
		}
		return Root{}, errors.Wrap(err, "error parsing YAML in config file at "+configPath)
	}

	return config, nil

View on GitHub (pinned to 2253eeeb25)

Solutions

  1. Pass an explicit path via --config /path/to/config.yml or set the TUNNEL_CONFIG environment variable
  2. Verify the variable or flag feeding configPath is not empty before calling the manager
  3. Create the expected default config file (~/.cloudflared/config.yml or /etc/cloudflared/config.yml) if you intend defaults to work

Example fix

// before
root, err := readConfigFromPath(cfgPath, log) // cfgPath == ""
// after
if cfgPath == "" {
    cfgPath = "/etc/cloudflared/config.yml"
}
root, err := readConfigFromPath(cfgPath, log)
Defensive patterns

Strategy: validation

Validate before calling

if configPath == "" {
    return errors.New("no cloudflared config path provided; use --config or create ~/.cloudflared/config.yml")
}
if _, err := os.Stat(configPath); os.IsNotExist(err) {
    return fmt.Errorf("config file %s does not exist", configPath)
}

Type guard

func hasReadableConfig(path string) bool {
    if path == "" { return false }
    info, err := os.Stat(path)
    return err == nil && !info.IsDir()
}

Try / catch

root, err := readConfigFromPath(cfgPath, log)
if err != nil && err.Error() == "unable to find config file" {
    // prompt for --config or fall back to default config location
}

Prevention

When it happens

Trigger: A caller (e.g. config manager code watching/loading config files) invokes readConfigFromPath with configPath == "", typically because no config path was resolved from flags or defaults.

Common situations: --config flag not set and no default config file found so the resolved path is empty; environment or service-manager setups that pass an unset variable as the config path; programmatic use of the config manager.

Related errors


AI-assisted analysis of cloudflare/cloudflared@2253eeeb25 (2026-09-06). Data as JSON: /api/errors/a7c65c82a0df169e. Report an issue: GitHub.