OpenNHP/opennhp · warning

load relay peer config err (optional)

Error message

load relay peer config err (optional): %v

What it means

Identical optional-load pattern to db.toml: during NHP-Server startup, loadPeers attempts to read <ExeDirPath>/etc/relay.toml containing NHP-Relay peer definitions. If loadConfigFile fails, the server logs 'load relay peer config err (optional): %v' at Warning level and continues without relay peers; the file-based watcher is also skipped. NHP-Relay support is thus opt-in per deployment.

Solutions

  1. If relays are used, create <ExeDirPath>/etc/relay.toml with valid [[Relays]] entries (Id, Addr, PublicKey) and restart.
  2. If relays are not used, ignore the warning — the config is explicitly optional.
  3. Confirm the daemon's working directory so ExeDirPath/etc contains relay.toml.
  4. Check the file is readable by the daemon's runtime user.
  5. Verify TOML validity — if it parses but [[Relays]] is wrong, updateRelayPeers errors appear separately.

Example fix

# before: missing optional file triggers warning
$ ls etc/
config.toml  agent.toml  db.toml

# after: provision relay.toml
# etc/relay.toml
[[Relays]]
Id = "relay-1"
Addr = "10.0.0.9:6110"
PublicKey = "..."
Defensive patterns

Strategy: validation

Validate before calling

# check the optional config exists and parses before starting the server
f="$EXE_DIR/etc/relay.toml"
if [ -f "$f" ]; then
    python3 -c "import tomllib;tomllib.load(open('$f','rb'))" || echo "relay.toml invalid TOML"
else
    echo "relay.toml absent — relay peers disabled (warning expected)"
fi

Type guard

// Go: probe the optional file exactly as loadPeers does
func relayPeerConfigAvailable() bool {
    _, err := os.Stat(filepath.Join(utils.ExeDirPath, "etc", "relay.toml"))
    return err == nil
}

Prevention

When it happens

Trigger: s.loadConfigFile on <ExeDirPath>/etc/relay.toml fails during Start -> loadPeers: (1) relay.toml absent because relays are not deployed, (2) the daemon runs from a directory whose etc/ lacks relay.toml, (3) read/permission failure on the file.

Common situations: Deployments without TCP relay functionality omitting relay.toml; running nhp-serverd from a wrong working directory so ExeDirPath points elsewhere; config templating (e.g. envsubst renderers) that did not emit relay.toml; file permission issues in containers; and consequently server failing to forward via relays it does not know about.

Understand the failure class

Background: "Config file not found": what it means and how to fix it in docker-sync, Maven, Vagrant, Turborepo and other tools — this error's family across 60 libraries.

Related errors


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

Appendix: source

Thrown at endpoints/server/config.go:347

		if updateErr := s.updateDePeers(dePeers.DBs); updateErr != nil {
			// ignore error
			_ = updateErr
		}
		dbConfigWatch = utils.WatchFile(fileNameDE, func() {
			log.Info("device peer config: %s has been updated", fileNameDE)
			if contentDE, err = s.loadConfigFile(fileNameDE); err == nil {
				if err = toml.Unmarshal(contentDE, &dePeers); err == nil {
					_ = s.updateDePeers(dePeers.DBs)
				}
			}
		})
	}

	// relay.toml (optional)
	fileNameRelay := filepath.Join(ExeDirPath, "etc", "relay.toml")
	contentRelay, err := s.loadConfigFile(fileNameRelay)
	if err != nil {
		log.Warning("load relay peer config err (optional): %v", err)
	} else {
		var relayPeers Peers
		if unmarshalErr := toml.Unmarshal(contentRelay, &relayPeers); unmarshalErr != nil {
			log.Error("failed to unmarshal relay peers config: %v", unmarshalErr)
		}
		if updateErr := s.updateRelayPeers(relayPeers.Relays); updateErr != nil {
			_ = updateErr
		}
		relayConfigWatch = utils.WatchFile(fileNameRelay, func() {
			log.Info("relay peer config: %s has been updated", fileNameRelay)
			if contentRelay, err = s.loadConfigFile(fileNameRelay); err == nil {
				var relayPeers Peers
				if err = toml.Unmarshal(contentRelay, &relayPeers); err == nil {
					_ = s.updateRelayPeers(relayPeers.Relays)
				}
			}
		})
	}

View on GitHub (pinned to 6e04ca5ff0)