OpenNHP/opennhp · critical

failed to create device

Error message

failed to create device %v

What it means

core.NewDevice(NHP_SERVER, prk, option) returned nil, meaning the core device could not be initialized from the decoded private key, so Start fails with this error. The log also prints the accompanying err (which may be nil if NewDevice fails silently on an invalid key).

Solutions

  1. Regenerate a fresh key pair with nhp-serverd keygen and redeploy the private key
  2. Verify the decoded key length matches the cipher scheme requirement (32 bytes for curve25519)
  3. Check for err being nil in the log line — if so the key bytes are structurally invalid
  4. Ensure the key was not base64-decoded twice or truncated during templating

Example fix

// before
prk = short/truncated base64 -> decoded 12 bytes
// after
nhp-serverd keygen --curve --json  # then copy privateKeyBase64 exactly
Defensive patterns

Strategy: validation

Validate before calling

key, err := base64.StdEncoding.DecodeString(cfg.PrivateKeyBase64)
if err != nil || len(key) != 32 {
    return fmt.Errorf("device key must decode to 32 bytes, got %d (err=%v)", len(key), err)
}

Try / catch

if err := server.Start(); err != nil {
    if strings.Contains(err.Error(), "failed to create device") {
        return fmt.Errorf("regenerate server keys with 'keygen' and redeploy: %w", err)
    }
    return err
}

Prevention

When it happens

Trigger: The base64-decoded private key has the wrong length or is invalid for the selected cipher scheme (e.g. not a 32-byte Curve25519/SM2 key), or the key is all zeros/rejected by validation.

Common situations: Truncated key from a copy-paste; key generated for a different cipher scheme than the peers expect; empty key after failed decode handled earlier but of wrong size.

Understand the failure class

Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.

Related errors


AI-assisted analysis of OpenNHP/opennhp@6e04ca5ff0 (2026-09-07). Data as JSON: /api/errors/4cb8044d10712494. Report an issue: GitHub.

Appendix: source

Thrown at endpoints/server/udpserver.go:275

	s.listenAddr, err = net.ResolveUDPAddr(laddr.Network(), laddr.String())
	if err != nil {
		log.Error("resolve local UDPAddr error: %v", err)
		return fmt.Errorf("resolve UDPAddr error %v", err)
	}

	prk, err := base64.StdEncoding.DecodeString(s.config.PrivateKeyBase64)
	if err != nil {
		log.Error("private key parse error: %v", err)
		return fmt.Errorf("private key parse error %v", err)
	}

	option := &core.DeviceOptions{
		DisableAgentPeerValidation: s.config.DisableAgentValidation,
	}
	s.device = core.NewDevice(core.NHP_SERVER, prk, option)
	if s.device == nil {
		log.Critical("failed to create device: %v", err)
		return fmt.Errorf("failed to create device %v", err)
	}

	// Stateless cookie signing key. In a multi-instance cluster all
	// nhp-server replicas must share the same value so any of them can
	// verify a cookie that a sibling minted. When the operator hasn't
	// configured one we mint a random per-process key — fine for a single
	// instance, broken for a cluster (the failure is silent: cookies
	// minted by replica A don't verify on replica B and the agent's RKN
	// stalls until timeout). Always log which mode we're in.
	cookieKey, cookieKeyErr := decodeCookieSigningKey(s.config.CookieSigningKeyBase64)
	if cookieKeyErr != nil {
		// Malformed (not empty) is an ops mistake — fail fast rather
		// than silently degrading to a per-process random key. Silent
		// fallback would let a cluster look healthy while its replicas
		// each mint cookies a sibling can't verify.
		log.Critical("invalid CookieSigningKeyBase64 in config: %v", cookieKeyErr)
		return fmt.Errorf("invalid CookieSigningKeyBase64: %w", cookieKeyErr)
	}

View on GitHub (pinned to 6e04ca5ff0)