OpenNHP/opennhp · error

relay: failed to parse config

Error message

relay: failed to parse config %s: %w

What it means

LoadConfig reads the relay TOML file and hands it to toml.Unmarshal; this error wraps any parse failure with the file path. It means the config file exists but is not valid TOML or does not match the Config struct (e.g. wrong types), so the relay cannot build its runtime configuration. It is thrown by endpoints/relay/config.go's LoadConfig, which the app calls during startup.

Solutions

  1. Run the file through a TOML linter/parser (e.g. tomlq or an online validator) to find the syntax error on the reported line
  2. Compare field names/types in config.toml against the Config struct in endpoints/relay/config.go
  3. Restore the file from the repo's example/template config and re-apply changes incrementally
  4. Check that the file was not corrupted or truncated in transfer (diff against a known-good copy)

Example fix

// before (invalid TOML)
privateKeyBase64 = abc123
[Servers]
pubKeyBase64 = "..."
// after
privateKeyBase64 = "abc123"
[[Servers]]
pubKeyBase64 = "..."
Defensive patterns

Strategy: validation

Validate before calling

// Go: validate config TOML before startup
data, err := os.ReadFile(path)
if err != nil { return err }
var probe map[string]any
if _, err := toml.Unmarshal(data, &probe); err != nil {
	return fmt.Errorf("config %s is not valid TOML: %w", path, err)
}
cfg, err := relay.LoadConfig(path)
if err != nil { log.Fatalf("relay config error: %v", err) }

Try / catch

cfg, err := relay.LoadConfig(path)
if err != nil {
	return fmt.Errorf("startup aborted, fix relay config: %w", err)
}

Prevention

When it happens

Trigger: LoadConfig is called by runApp at startup with a path to a config.toml whose contents fail toml.Unmarshal — malformed TOML syntax (missing quotes, bad indentation, duplicate keys) or a type mismatch with the Config struct (e.g. string where an integer port is expected).

Common situations: Hand-editing config.toml and introducing a TOML syntax error; copying a Linux config to another environment with a mangled encoding; renaming a table key like [Servers] incorrectly; using TOML 1.0 features unsupported by the pinned toml library version.

Understand the failure class

Background: Schema validation failed / invalid input schema: payload rejected because its shape doesn't match the expected schema — this error's family across 28 libraries.

Related errors


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

Appendix: source

Thrown at endpoints/relay/config.go:161

// 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.
func (cfg *Config) normalize() error {
	if cfg.PrivateKeyBase64 == "" {
		return fmt.Errorf("relay: privateKeyBase64 must be set in config")
	}

View on GitHub (pinned to 6e04ca5ff0)