{"record":{"id":"abf04d68846bb180","repo":"OpenNHP/opennhp","slug":"private-key-parse-error-v-abf04d","errorCode":null,"errorMessage":"private key parse error %v","messagePattern":"private key parse error (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"endpoints/server/udpserver.go","lineNumber":266,"sourceCode":"\t\tPort: s.config.ListenPort,\n\t})\n\tif err != nil {\n\t\tlog.Error(\"listen error: %v\", err)\n\t\treturn fmt.Errorf(\"listen error %v\", err)\n\t}\n\n\t// retrieve local port\n\tladdr := s.listenConn.LocalAddr()\n\ts.listenAddr, err = net.ResolveUDPAddr(laddr.Network(), laddr.String())\n\tif err != nil {\n\t\tlog.Error(\"resolve local UDPAddr error: %v\", err)\n\t\treturn fmt.Errorf(\"resolve UDPAddr error %v\", err)\n\t}\n\n\tprk, err := base64.StdEncoding.DecodeString(s.config.PrivateKeyBase64)\n\tif err != nil {\n\t\tlog.Error(\"private key parse error: %v\", err)\n\t\treturn fmt.Errorf(\"private key parse error %v\", err)\n\t}\n\n\toption := &core.DeviceOptions{\n\t\tDisableAgentPeerValidation: s.config.DisableAgentValidation,\n\t}\n\ts.device = core.NewDevice(core.NHP_SERVER, prk, option)\n\tif s.device == nil {\n\t\tlog.Critical(\"failed to create device: %v\", err)\n\t\treturn fmt.Errorf(\"failed to create device %v\", err)\n\t}\n\n\t// Stateless cookie signing key. In a multi-instance cluster all\n\t// nhp-server replicas must share the same value so any of them can\n\t// verify a cookie that a sibling minted. When the operator hasn't\n\t// configured one we mint a random per-process key — fine for a single\n\t// instance, broken for a cluster (the failure is silent: cookies\n\t// minted by replica A don't verify on replica B and the agent's RKN\n\t// stalls until timeout). Always log which mode we're in.","sourceCodeStart":248,"sourceCodeEnd":284,"githubUrl":"https://github.com/OpenNHP/opennhp/blob/6e04ca5ff03222a699c24205cd4bf8fee9af7ffe/endpoints/server/udpserver.go#L248-L284","documentation":"Raised in nhp-server's UDP Start after the listen socket is bound: base64 decoding of config.PrivateKeyBase64 — the server's identity private key — failed. The daemon logs 'private key parse error' and returns, refusing to run with an unusable Noise-protocol identity.","triggerScenarios":"PrivateKeyBase64 contains whitespace, URL-safe base64 characters (-, _), raw binary pasted as text, or was generated by a tool using a different encoding.","commonSituations":"Operator pasting a hex or raw key instead of base64; config templating introducing quotes/newlines; keys generated with 'keygen --json' output field mixed up (public pasted as private).","solutions":["Regenerate keys with the daemon's keygen command (e.g. nhp-serverd keygen --curve --json) and paste the private key field verbatim","Validate with: echo \"$KEY\" | base64 -d >/dev/null && echo ok","Strip quotes, whitespace, and newlines from the configured value","Confirm you are using the private key, not the public key, and the correct cipher scheme"],"exampleFix":"// before\nprivateKeyBase64 = \"MFkwEwYHKoZI...\" // hex or PEM fragment\n// after\nprivateKeyBase64 = \"O2onvM62pC1io6jQKm8Nc2UyFXcd4kOmOsBIoYtB2ik=\"","handlingStrategy":"validation","validationCode":"key, err := base64.StdEncoding.DecodeString(cfg.PrivateKeyBase64)\nif err != nil || len(key) != expectedKeyLen {\n    return fmt.Errorf(\"privateKeyBase64 invalid: decode=%v len=%d\", err, len(key))\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Generate keys only via the daemon's keygen command","Paste the base64 value exactly as emitted, without quotes/newlines","Distinguish private vs public key fields","Confirm the cipher scheme matches the key type"],"tags":["config","base64","key","startup"],"backgroundTag":"invalid-config-value","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"}