OpenNHP/opennhp · error
failed to create device from new key
Error message
failed to create device from new key
What it means
ReinitWithKey stops the current device and builds a replacement from the supplied private key bytes via core.NewDevice; if construction fails it returns this error before swapping anything in. It is used by runRegisterApp after generating a fresh key pair for registration.
Solutions
- Ensure the key came from a successful keygen/curve25519 generation (32 decoded bytes), and pass the decoded bytes, not the base64 string bytes
- Check the code path that generated privKeyBytes for an ignored error or empty result
- Log the byte length of privKeyBytes before calling ReinitWithKey to confirm validity
Example fix
// before
a.ReinitWithKey([]byte(privKeyB64), cipherScheme)
// after
prk, err := base64.StdEncoding.DecodeString(privKeyB64)
if err != nil { return err }
a.ReinitWithKey(prk, cipherScheme) Defensive patterns
Strategy: validation
Validate before calling
if len(privKeyBytes) != 32 {
return fmt.Errorf("ReinitWithKey: expected 32-byte key, got %d", len(privKeyBytes))
} Try / catch
if err := a.ReinitWithKey(prk, cipherScheme); err != nil {
log.Fatalf("new device from registration key failed: %v", err)
} Prevention
- Check the error from the key-generation call before using its output
- Pass decoded bytes, never base64 string bytes
- Assert key length before device creation
When it happens
Trigger: Calling ReinitWithKey (from runRegisterApp) with privKeyBytes that core.NewDevice cannot accept — nil slice, wrong length, or otherwise invalid key material.
Common situations: Key-generation step upstream returned empty/short bytes; passing a base64 string's bytes instead of decoded key bytes; cipher-scheme mismatch.
Related errors
- AuthServiceId is required
- no server cluster: set Cluster in resource.toml or use…
- registration failed
- registration failed
- failed to create device
AI-assisted analysis of OpenNHP/opennhp@6e04ca5ff0 (2026-09-07).
Data as JSON: /api/errors/66b8f6d076466335.
Report an issue: GitHub.
Appendix: source
Thrown at endpoints/agent/udpagent.go:578
func (a *UdpAgent) PublicKeyBase64ByCipherScheme() string {
if a.config.DefaultCipherScheme == common.CIPHER_SCHEME_GMSM {
return a.device.PublicKeyExBase64()
}
return a.device.PublicKeyBase64()
}
// PrivateKeyBase64 returns the agent's private key in base64 encoding.
func (a *UdpAgent) PrivateKeyBase64() string {
return a.config.PrivateKeyBase64
}
// ReinitWithKey stops the current device, creates a new one from the given
// private key bytes, and re-adds all known server peers. Call this after
// Start() when a fresh key pair has been generated for registration.
func (a *UdpAgent) ReinitWithKey(privKeyBytes []byte, cipherScheme int) error {
newDev := core.NewDevice(core.NHP_AGENT, privKeyBytes, nil)
if newDev == nil {
return fmt.Errorf("failed to create device from new key")
}
newDev.Start()
// Swap in the new device and repoint the receive channel atomically.
// Without repointing recvMsgCh, the old routine keeps reading the old
// (about-to-be-closed) DecryptedMsgQueue and nothing drains the new
// device's queue — cookie challenges and generic replies would be
// silently dropped after a reinit.
//
// Scope note: deviceMutex only synchronizes this swap window and the
// receive routine's channel capture. The send/knock/request paths and
// packetReceiveRoutine read a.device WITHOUT the lock. That is safe for
// the current one-shot register flow — ReinitWithKey runs right after
// Start() while senders are idle, and a.device is never rewritten
// afterward. If ReinitWithKey is ever called with active traffic, those
// readers must also take deviceMutex.
a.deviceMutex.Lock()
oldDev := a.deviceView on GitHub (pinned to 6e04ca5ff0)