OpenNHP/opennhp · error

relay: failed to read config

Error message

relay: failed to read config %s: %w

What it means

Raised by relay LoadConfig when os.ReadFile on the config path (the given path, or etc/config.toml under the executable directory) fails with an error other than not-exist — typically a permission problem, an unreadable directory, or an I/O error. A missing file is handled separately with a warning and defaults; this error means the file exists but cannot be read.

Solutions

  1. Check file permissions on the config path (chmod/chown so the relay process can read it)
  2. Confirm the path points to a regular file, not a directory or broken symlink
  3. If SELinux/AppArmor is enforced, allow the service to read its etc directory
  4. Fix the underlying I/O issue (disk, mount) if that is the cause
Defensive patterns

Strategy: validation

When it happens

Trigger: Thrown at endpoints/relay/config.go:157 when the library encounters an invalid state.

Common situations: See trigger scenarios.


AI-assisted analysis of OpenNHP/opennhp@6e04ca5ff0 (2026-09-07). Data as JSON: /api/errors/1b501f010240ca2f. Report an issue: GitHub.

Appendix: source

Thrown at endpoints/relay/config.go:157

	}
}

// LoadConfig reads a TOML config file.  If path is empty it defaults to
// etc/config.toml relative to ExeDirPath.
func LoadConfig(path string) (*Config, error) {
	cfg := DefaultConfig()

	if path == "" {
		path = filepath.Join(ExeDirPath, "etc", "config.toml")
	}

	data, err := os.ReadFile(path)
	if err != nil {
		if os.IsNotExist(err) {
			log.Warning("[Relay] config file %s not found, using defaults", path)
			return cfg, nil
		}
		return nil, fmt.Errorf("relay: failed to read config %s: %w", path, err)
	}

	if err := toml.Unmarshal(data, cfg); err != nil {
		return nil, fmt.Errorf("relay: failed to parse config %s: %w", path, err)
	}

	if err := cfg.normalize(); err != nil {
		return nil, err
	}

	log.Info("[Relay] loaded config from %s with %d server(s)", path, len(cfg.Servers))
	return cfg, nil
}

// normalize validates the configuration and applies legacy-field migration so
// that the rest of the relay only has to look at Config.Servers. It is
// exported as a method (not a function) to make it directly testable on a
// hand-built Config in unit tests without round-tripping through TOML.

View on GitHub (pinned to 6e04ca5ff0)