OpenNHP/opennhp · error
config load error
Error message
config load error
What it means
errLoadConfig is a package-level sentinel error in endpoints/db/config.go (mirrored by endpoints/ac/config.go) returned whenever the daemon's base/server/tees/resource config load or watch-update fails. It is also used as the panic-recovery value in updateBaseConfig, so any panic during a config reload is reported as this generic error. Because it is a plain sentinel, the underlying cause must be found in logs.
Solutions
- Validate each TOML file in etc/ (config.toml, server.toml, tees.toml, resource.toml) parses correctly.
- Check daemon logs immediately before the error for the real parse/watch failure.
- Restore file permissions and ensure the daemon user can read all config files.
- Compare configs against the shipped templates in endpoints/*/etc/ for field names and types.
- If triggered by a watch update, fix the last written change or redeploy the previous known-good config.
Example fix
// before: panic swallowed into generic sentinel
utils.CatchPanicThenRun(func() {
err = errLoadConfig
})
// after: record the concrete cause too
utils.CatchPanicThenRun(func() {
log.Error("panic during config update: %v", recover())
err = fmt.Errorf("config load error: %w", errLoadConfig)
}) Defensive patterns
Strategy: validation
Validate before calling
for _, f := range []string{"config.toml", "server.toml", "tees.toml", "resource.toml"} {
if _, err := toml.ParseFile(filepath.Join(etcDir, f)); err != nil {
log.Fatalf("config %s invalid: %v", f, err)
}
} Type guard
func configReadable(dir string) bool {
for _, f := range requiredConfigFiles {
if fi, err := os.Stat(filepath.Join(dir, f)); err != nil || fi.IsDir() { return false }
}
return true
} Try / catch
if err := ac.LoadConfig(); err != nil {
if errors.Is(err, errLoadConfig) {
log.Fatalf("config load failed — inspect logs and etc/*.toml: %v", err)
}
return err
} Prevention
- Validate TOML files in CI before deploying configs.
- Write config updates atomically (temp file + rename) so watchers never parse partial files.
- Run the daemon as a user with read access to the etc/ directory.
- Diff live configs against shipped templates after upgrades.
When it happens
Trigger: LoadConfig or updateBaseConfig fails: malformed/unreadable config.toml/server.toml/tees.toml/resource.toml, watch callback panics (recovered and converted to errLoadConfig), or type-conversion failures while parsing fields like LogLevel.
Common situations: TOML file edited by hand with syntax errors; missing config file at startup; wrong field types (string where int expected); etcd/file watcher delivering a partially written file that panics the parser; permissions changed on etc/ directory.
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
- server peer config invalid on initial load
- relay: failed to parse config
- load db peer config err (optional)
- load relay peer config err (optional)
- private key parse error
AI-assisted analysis of OpenNHP/opennhp@6e04ca5ff0 (2026-09-07).
Data as JSON: /api/errors/bc20b7d8e7fc66c4.
Report an issue: GitHub.
Appendix: source
Thrown at endpoints/db/config.go:23
"io"
"os"
"path/filepath"
toml "github.com/pelletier/go-toml/v2"
"github.com/OpenNHP/opennhp/nhp/common/clusterconfig"
"github.com/OpenNHP/opennhp/nhp/core"
"github.com/OpenNHP/opennhp/nhp/log"
"github.com/OpenNHP/opennhp/nhp/utils"
)
var (
baseConfigWatch io.Closer
serverConfigWatch io.Closer
teesConfigWatch io.Closer
resourceConfigWatch io.Closer
errLoadConfig = fmt.Errorf("config load error")
)
type Config struct {
LogLevel int
PrivateKeyBase64 string
DefaultCipherScheme int `json:"defaultCipherScheme"`
SymmetricCipherMode string `json:"symmetricCipherMode"`
DbId string `json:"dbId"`
}
// Peers is the top-level shape of server.toml. Each entry is one
// logical nhp-server identity (one pubkey) reachable at 1..N instances
// — same schema as nhp-agent's and nhp-ac's server.toml (see
// nhp/common/clusterconfig). nhp-db only ever talks to a single
// instance per cluster today, so LoadBalance / StickyInstance are
// loaded but ignored; the schema is shared so operators don't need a
// per-daemon dialect.
type Peers struct {View on GitHub (pinned to 6e04ca5ff0)