OpenNHP/opennhp · critical
failed to create device
Error message
failed to create device %v
What it means
After the private key decodes, Start calls core.NewDevice(core.NHP_DB, prk, nil). NewDevice returns nil when it cannot construct the protocol device (e.g. the decoded key material is rejected at the crypto layer or an internal invariant fails). Start logs a critical message and returns 'failed to create device <err>' so the daemon exits instead of running half-initialized.
Solutions
- Regenerate the identity key with `nhp-device keygen` and replace privateKeyBase64 in config.toml
- Verify the key length/type matches the device's cipher scheme (SM2 vs Curve25519)
- Diff the configured key against the last known-good value in your secret store
- Check the accompanying log.Critical output for the underlying device-construction cause
Example fix
// before (config.toml, decodes but invalid key bytes) privateKeyBase64 = "AAAA" // after privateKeyBase64 = "<valid key from `nhp-device keygen --json`>"
Defensive patterns
Strategy: try-catch
Validate before calling
const key = Buffer.from(config.privateKeyBase64, 'base64');
// reject obviously invalid key material before startup
if (key.length === 0 || key.every(b => b === 0)) {
throw new Error('decoded private key is empty or all zeros; regenerate with nhp-device keygen');
} Type guard
const isPlausibleKeyMaterial = (b64) => { const b = Buffer.from(b64 ?? '', 'base64'); return b.length >= 32 && !b.every(x => x === 0); }; Try / catch
if err := device.Start(); err != nil {
if strings.HasPrefix(err.Error(), "failed to create device") {
log.Fatalf("device init failed (%v): regenerate keys with `nhp-device keygen` and update config", err)
}
return err
} Prevention
- Regenerate keys with `nhp-device keygen` whenever device init fails
- Keep cipher scheme consistent between key generation and device config
- Restore keys only from verified secret-store backups
- Fail fast in deployment scripts by starting the device and checking for immediate exit
When it happens
Trigger: Calling Start (via `nhp-device run`) where core.NewDevice returns nil — typically because the decoded private key bytes are structurally invalid for the device's cipher scheme even though base64 decoding succeeded.
Common situations: A base64 string that decodes but is not a valid SM2/Curve25519 private key (wrong byte length, all zeros, a public key mistakenly configured); keys migrated between cipher schemes; corrupted key files restored from backups.
Related errors
- failed to create device
- invalid input key
- private key parse error
- failed to create device
- missing remote peer public key
AI-assisted analysis of OpenNHP/opennhp@6e04ca5ff0 (2026-09-07).
Data as JSON: /api/errors/ff979637ade19303.
Report an issue: GitHub.
Appendix: source
Thrown at endpoints/db/udpdevice.go:145
log.Info("=== REVISION %s ===", version.CommitId)
log.Info("=== RELEASE %s ===", version.BuildTime)
log.Info("=========================================================")
err = a.loadBaseConfig()
if err != nil {
return err
}
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_DB, prk, nil)
if a.device == nil {
log.Critical("failed to create device %v\n", err)
return fmt.Errorf("failed to create device %v", err)
}
a.remoteConnectionMap = make(map[string]*UdpConn)
a.serverPeerMap = make(map[string]*core.UdpPeer)
// load peers
_ = a.loadPeers()
// load TEEs
_ = a.loadTEEs()
a.signals.stop = make(chan struct{})
a.signals.serverMapUpdated = make(chan struct{}, 1)
a.recvMsgCh = a.device.DecryptedMsgQueue
a.sendMsgCh = make(chan *core.MsgData, core.SendQueueSize)
// start device routines
a.device.Start()View on GitHub (pinned to 6e04ca5ff0)