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

  1. Ensure the key came from a successful keygen/curve25519 generation (32 decoded bytes), and pass the decoded bytes, not the base64 string bytes
  2. Check the code path that generated privKeyBytes for an ignored error or empty result
  3. 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

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


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.device

View on GitHub (pinned to 6e04ca5ff0)