OpenNHP/opennhp · error
private key parse error
Error message
private key parse error %v
What it means
The agent's Start decodes config.PrivateKeyBase64 with base64.StdEncoding; if the string is not valid base64 it returns this error instead of creating a device. It guards against corrupted or hand-edited key material producing a broken identity.
Solutions
- Regenerate the key with the daemon's keygen --curve --json command and paste the value verbatim
- Strip whitespace/newlines/quotes from PrivateKeyBase64
- Check the encoding: std base64 required; re-encode any base64url key (strings.ReplaceAll - and _ with + and /) before storing
Example fix
// before PrivateKeyBase64 = """dJf3 key==""" // after PrivateKeyBase64 = "dJf3key=="
Defensive patterns
Strategy: validation
Validate before calling
if _, err := base64.StdEncoding.DecodeString(cfg.PrivateKeyBase64); err != nil {
return fmt.Errorf("invalid PrivateKeyBase64: %w", err)
} Try / catch
if err := agent.Start(); err != nil {
var parseErr error
if strings.HasPrefix(err.Error(), "private key parse error") {
log.Fatalf("fix base64 key in config.toml: %v", err)
}
_ = parseErr
} Prevention
- Paste keygen --json output programmatically instead of by hand
- Trim whitespace and strip quotes when loading the key value
- Never hand-edit base64 key material in editors that wrap lines
When it happens
Trigger: Start (via runApp, runDHPApp, runRegisterApp, RestartAgent) with a PrivateKeyBase64 value containing whitespace, quotes, newlines, hex instead of base64, or a base64url-encoded string with '-'/'_' characters.
Common situations: Copy-pasting a key with surrounding quotes or trailing newline; generating the key with a tool that outputs base64url; manual edits to config.toml mangling the key.
Understand the failure class
Background: "Invalid ... format", "must be in format X", "does not look like a ..." — invalid argument format errors across CLI tools and libraries — this error's family across 17 libraries.
- Parsing and encoding errors: unexpected token, malformed input — why parsers reject input and how to find the real culprit.
Related errors
AI-assisted analysis of OpenNHP/opennhp@6e04ca5ff0 (2026-09-07).
Data as JSON: /api/errors/45a48013c6263378.
Report an issue: GitHub.
Appendix: source
Thrown at endpoints/agent/udpagent.go:392
}
var prk []byte
if a.config.PrivateKeyBase64 == "" {
// An empty private key is only acceptable in the register bootstrap
// flow (allowMissingConfig): use a throwaway key that ReinitWithKey
// replaces immediately after Start returns. For run/dhp this is a
// real misconfiguration — fail loudly instead of silently starting
// with a random key and empty identity.
if !a.allowMissingConfig {
log.Error("no private key configured in etc/config.toml")
return fmt.Errorf("no private key configured; check etc/config.toml")
}
prk = core.NewECDH(core.ECC_CURVE25519).PrivateKey()
} else {
prk, err = base64.StdEncoding.DecodeString(a.config.PrivateKeyBase64)
if err != nil {
log.Error("private key parse error %v\n", err)
return fmt.Errorf("private key parse error %v", err)
}
}
a.device = core.NewDevice(core.NHP_AGENT, prk, nil)
if a.device == nil {
log.Critical("failed to create device %v\n", err)
return fmt.Errorf("failed to create device %v", err)
}
// start device routines
a.device.Start()
// serverClusterMap must be non-nil before loadPeers runs (so
// updateServerPeers' map-swap has a base value) and before
// callers like AddServer / GetFirstServerPeer touch it on
// agents that haven't loaded a server.toml yet.
a.serverClusterMap = make(map[string]*ServerCluster)
a.serverClusterByName = make(map[string]*ServerCluster)View on GitHub (pinned to 6e04ca5ff0)