OpenNHP/opennhp · critical
failed to create device
Error message
failed to create device %v
What it means
After decoding the private key, Start calls core.NewDevice(NHP_AGENT, prk, nil); if that returns nil the agent cannot build its device (ECDH context / device identity) and Start fails. This wraps an internal device-construction failure, typically from invalid key length or cipher/crypto initialization.
Solutions
- Regenerate a fresh key pair with keygen --curve and use it
- Verify the decoded key is 32 bytes (curve25519)
- Check agent/server logs (log.Critical line) and the nhp/core NewDevice code for the exact nil-return condition
Example fix
// before
prk = []byte("shortkey")
// after
prk, _ := base64.StdEncoding.DecodeString("<32-byte base64 key from keygen>") Defensive patterns
Strategy: try-catch
Validate before calling
prk, err := base64.StdEncoding.DecodeString(cfg.PrivateKeyBase64)
if err != nil || len(prk) != 32 {
return fmt.Errorf("agent key must decode to 32 bytes, got %d", len(prk))
} Try / catch
if err := agent.Start(); err != nil {
if strings.Contains(err.Error(), "failed to create device") {
log.Fatalf("device init failed (%v); regenerate key with keygen --curve", err)
}
} Prevention
- Validate decoded key length is 32 bytes before Start
- Regenerate keys with the bundled keygen tool rather than external tools
- Keep agent and core library versions aligned
When it happens
Trigger: Start with a private key that decodes from base64 but has an invalid byte length or otherwise fails ECDH initialization inside core.NewDevice.
Common situations: Truncated key from a partial copy-paste; key generated for a different cipher scheme with wrong byte size; corrupted key file.
Related errors
- failed to create device from new key
- keystore: generate otp
- failed to create blake2s hash
- failed to create AES-GCM
- unsupported cipher type for CBC
AI-assisted analysis of OpenNHP/opennhp@6e04ca5ff0 (2026-09-07).
Data as JSON: /api/errors/3be4c41ef2a9bf04.
Report an issue: GitHub.
Appendix: source
Thrown at endpoints/agent/udpagent.go:399
// 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)
// load peers
_ = a.loadPeers()
a.remoteConnectionMap = make(map[string]*UdpConn)
a.signals.stop = make(chan struct{})View on GitHub (pinned to 6e04ca5ff0)