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
- If relays are used, create <ExeDirPath>/etc/relay.toml with valid [[Relays]] entries (Id, Addr, PublicKey) and restart.
- If relays are not used, ignore the warning — the config is explicitly optional.
- Confirm the daemon's working directory so ExeDirPath/etc contains relay.toml.
- Check the file is readable by the daemon's runtime user.
- 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
- Provision etc/relay.toml whenever TCP relays are part of the deployment topology
- Run nhp-serverd from the directory containing its etc/ tree so ExeDirPath resolves correctly
- Include relay.toml in config templating/rendering pipelines (e.g. envsubst deploy configs)
- Keep the file readable by the daemon's runtime user
- Treat this warning as expected in relay-less deployments — document it in the runbook
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
- load db peer config err (optional)
- server peer config invalid on initial load
- config load error
- relay: failed to parse config
- private key parse error
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)