{"record":{"id":"25d3a7a09c1bc193","repo":"OpenNHP/opennhp","slug":"load-relay-peer-config-err-optional-v","errorCode":null,"errorMessage":"load relay peer config err (optional): %v","messagePattern":"load relay peer config err \\(optional\\): (.+?)","errorType":"console","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"endpoints/server/config.go","lineNumber":347,"sourceCode":"\t\tif updateErr := s.updateDePeers(dePeers.DBs); updateErr != nil {\n\t\t\t// ignore error\n\t\t\t_ = updateErr\n\t\t}\n\t\tdbConfigWatch = utils.WatchFile(fileNameDE, func() {\n\t\t\tlog.Info(\"device peer config: %s has been updated\", fileNameDE)\n\t\t\tif contentDE, err = s.loadConfigFile(fileNameDE); err == nil {\n\t\t\t\tif err = toml.Unmarshal(contentDE, &dePeers); err == nil {\n\t\t\t\t\t_ = s.updateDePeers(dePeers.DBs)\n\t\t\t\t}\n\t\t\t}\n\t\t})\n\t}\n\n\t// relay.toml (optional)\n\tfileNameRelay := filepath.Join(ExeDirPath, \"etc\", \"relay.toml\")\n\tcontentRelay, err := s.loadConfigFile(fileNameRelay)\n\tif err != nil {\n\t\tlog.Warning(\"load relay peer config err (optional): %v\", err)\n\t} else {\n\t\tvar relayPeers Peers\n\t\tif unmarshalErr := toml.Unmarshal(contentRelay, &relayPeers); unmarshalErr != nil {\n\t\t\tlog.Error(\"failed to unmarshal relay peers config: %v\", unmarshalErr)\n\t\t}\n\t\tif updateErr := s.updateRelayPeers(relayPeers.Relays); updateErr != nil {\n\t\t\t_ = updateErr\n\t\t}\n\t\trelayConfigWatch = utils.WatchFile(fileNameRelay, func() {\n\t\t\tlog.Info(\"relay peer config: %s has been updated\", fileNameRelay)\n\t\t\tif contentRelay, err = s.loadConfigFile(fileNameRelay); err == nil {\n\t\t\t\tvar relayPeers Peers\n\t\t\t\tif err = toml.Unmarshal(contentRelay, &relayPeers); err == nil {\n\t\t\t\t\t_ = s.updateRelayPeers(relayPeers.Relays)\n\t\t\t\t}\n\t\t\t}\n\t\t})\n\t}","sourceCodeStart":329,"sourceCodeEnd":365,"githubUrl":"https://github.com/OpenNHP/opennhp/blob/6e04ca5ff03222a699c24205cd4bf8fee9af7ffe/endpoints/server/config.go#L329-L365","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"# before: missing optional file triggers warning\n$ ls etc/\nconfig.toml  agent.toml  db.toml\n\n# after: provision relay.toml\n# etc/relay.toml\n[[Relays]]\nId = \"relay-1\"\nAddr = \"10.0.0.9:6110\"\nPublicKey = \"...\"","handlingStrategy":"validation","validationCode":"# check the optional config exists and parses before starting the server\nf=\"$EXE_DIR/etc/relay.toml\"\nif [ -f \"$f\" ]; then\n    python3 -c \"import tomllib;tomllib.load(open('$f','rb'))\" || echo \"relay.toml invalid TOML\"\nelse\n    echo \"relay.toml absent — relay peers disabled (warning expected)\"\nfi","typeGuard":"// Go: probe the optional file exactly as loadPeers does\nfunc relayPeerConfigAvailable() bool {\n    _, err := os.Stat(filepath.Join(utils.ExeDirPath, \"etc\", \"relay.toml\"))\n    return err == nil\n}","tryCatchPattern":null,"preventionTips":["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"],"tags":["config","toml","optional","startup"],"backgroundTag":"config-file-not-found","analyzedSha":"6e04ca5ff03222a699c24205cd4bf8fee9af7ffe","analyzedAt":"2026-09-07T15:44:59.941Z","contentChangedAt":"2026-09-07T15:44:59.941Z","schemaVersion":2},"datasetVersion":"2026-09-16T04:17:20.429Z"}