{"record":{"id":"45a48013c6263378","repo":"OpenNHP/opennhp","slug":"private-key-parse-error-v-45a480","errorCode":null,"errorMessage":"private key parse error %v","messagePattern":"private key parse error (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"endpoints/agent/udpagent.go","lineNumber":392,"sourceCode":"\t}\n\n\tvar prk []byte\n\tif a.config.PrivateKeyBase64 == \"\" {\n\t\t// An empty private key is only acceptable in the register bootstrap\n\t\t// flow (allowMissingConfig): use a throwaway key that ReinitWithKey\n\t\t// replaces immediately after Start returns. For run/dhp this is a\n\t\t// real misconfiguration — fail loudly instead of silently starting\n\t\t// with a random key and empty identity.\n\t\tif !a.allowMissingConfig {\n\t\t\tlog.Error(\"no private key configured in etc/config.toml\")\n\t\t\treturn fmt.Errorf(\"no private key configured; check etc/config.toml\")\n\t\t}\n\t\tprk = core.NewECDH(core.ECC_CURVE25519).PrivateKey()\n\t} else {\n\t\tprk, err = base64.StdEncoding.DecodeString(a.config.PrivateKeyBase64)\n\t\tif err != nil {\n\t\t\tlog.Error(\"private key parse error %v\\n\", err)\n\t\t\treturn fmt.Errorf(\"private key parse error %v\", err)\n\t\t}\n\t}\n\n\ta.device = core.NewDevice(core.NHP_AGENT, prk, nil)\n\tif a.device == nil {\n\t\tlog.Critical(\"failed to create device %v\\n\", err)\n\t\treturn fmt.Errorf(\"failed to create device %v\", err)\n\t}\n\n\t// start device routines\n\ta.device.Start()\n\n\t// serverClusterMap must be non-nil before loadPeers runs (so\n\t// updateServerPeers' map-swap has a base value) and before\n\t// callers like AddServer / GetFirstServerPeer touch it on\n\t// agents that haven't loaded a server.toml yet.\n\ta.serverClusterMap = make(map[string]*ServerCluster)\n\ta.serverClusterByName = make(map[string]*ServerCluster)","sourceCodeStart":374,"sourceCodeEnd":410,"githubUrl":"https://github.com/OpenNHP/opennhp/blob/6e04ca5ff03222a699c24205cd4bf8fee9af7ffe/endpoints/agent/udpagent.go#L374-L410","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"// before\nPrivateKeyBase64 = \"\"\"dJf3\nkey==\"\"\"\n// after\nPrivateKeyBase64 = \"dJf3key==\"","handlingStrategy":"validation","validationCode":"if _, err := base64.StdEncoding.DecodeString(cfg.PrivateKeyBase64); err != nil {\n    return fmt.Errorf(\"invalid PrivateKeyBase64: %w\", err)\n}","typeGuard":null,"tryCatchPattern":"if err := agent.Start(); err != nil {\n    var parseErr error\n    if strings.HasPrefix(err.Error(), \"private key parse error\") {\n        log.Fatalf(\"fix base64 key in config.toml: %v\", err)\n    }\n    _ = parseErr\n}","preventionTips":["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"],"tags":["config","base64","go","key-parsing"],"backgroundTag":"invalid-argument-format","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"}