OpenNHP/opennhp · error
peer expired (type= , pubkey= )
Error message
peer expired (type=%s, pubkey=%s)
What it means
The peer's public key was found in the pool, but the peer record reports itself expired (Peer.IsExpired). The device treats expiry as an authorization failure: the previously registered agent/server/AC must re-register or be refreshed before its messages are processed. This guards against stale registrations lingering after lease/renewal windows pass.
Solutions
- Have the peer re-register (resend NHP_REG/NHP_OTP) to refresh its record, then retry the operation.
- Check both hosts' clocks (NTP) — skew beyond the TTL makes valid peers look expired.
- Extend or disable the peer TTL in the peer-pool/registration store configuration if long-lived peers are expected.
- Purge and re-provision stale entries: restart the dynamic-registration store sync or re-run the key deployment for that peer.
Example fix
// before: agent resumes after TTL lapse with stale registration // agent client.Knock(...) // peer expired (type=agent, pubkey=...) // after: re-register first client.Register() // refreshes peer record/expiry client.Knock(...)
Defensive patterns
Strategy: retry
Validate before calling
// client-side: check remaining lease before sending
if time.Now().Unix() > registrationExpiryUnix-60 {
_ = client.Register() // refresh proactively
} Try / catch
err := client.SendKnock(server)
if err != nil && strings.Contains(err.Error(), "peer expired") {
if rerr := client.Register(); rerr == nil {
err = client.SendKnock(server)
}
} Prevention
- Schedule periodic re-registration well inside the peer TTL.
- Enforce NTP on all nodes to avoid clock-skew-driven expiry.
- Set TTLs longer than the longest expected maintenance/downtime window.
- Alert on expiry rejections so stale registrations are refreshed before users notice.
- Purge and re-provision expired entries in the registration store on startup.
When it happens
Trigger: An agent registered via NHP_REG whose expiry timestamp passed and that did not re-register; a long-running deployment where peer TTLs in the pool/SQLite store lapsed during downtime; clock skew between nodes making a still-valid peer look expired; a peer removed by a cleanup job while still in use.
Common situations: Agents offline longer than their registration TTL then resuming traffic; system clock jumps (NTP resets, VM snapshots) on the receiving node; server maintenance windows exceeding peer lease duration; expired entries in the dynamic-registration SQLite store never purged or renewed.
Related errors
- AuthServiceId is required
- no server cluster: set Cluster in resource.toml or use…
- registration failed
- registration failed
- failed to create device from new key
AI-assisted analysis of OpenNHP/opennhp@6e04ca5ff0 (2026-09-07).
Data as JSON: /api/errors/10a77f43dd60cdc7.
Report an issue: GitHub.
Appendix: source
Thrown at nhp/core/responder.go:510
// registered via NHP-REG stored in SQLite).
ppd.device.optionMutex.Lock()
fallback := ppd.device.option.PeerLookupFallback
ppd.device.optionMutex.Unlock()
if fallback != nil && fallback(peerPk, ppd.HeaderType) {
log.Info("validatePeer: %s peer accepted via fallback, pubkey=%s",
peerDeviceTypeName, peerPkBase64)
// Skip expiry/address checks for fallback peers.
goto peerAccepted
}
log.Error("validatePeer: %s peer not found in peer pool, pubkey=%s",
peerDeviceTypeName, peerPkBase64)
err = fmt.Errorf("peer not found in peer pool (type=%s, pubkey=%s)", peerDeviceTypeName, peerPkBase64)
return err
}
if peer.IsExpired() {
log.Error("validatePeer: %s peer expired, pubkey=%s", peerDeviceTypeName, peerPkBase64)
err = fmt.Errorf("peer expired (type=%s, pubkey=%s)", peerDeviceTypeName, peerPkBase64)
return err
}
if !ppd.ConnData.CheckRecvAddress(ppd.LocalInitTime, ppd.ConnData.RemoteAddr) {
log.Error("validatePeer: %s peer address mismatch on connection, pubkey=%s, remoteAddr=%s",
peerDeviceTypeName, peerPkBase64, ppd.ConnData.RemoteAddr)
err = fmt.Errorf("peer does not match its previous address on this connection (type=%s, pubkey=%s)", peerDeviceTypeName, peerPkBase64)
return err
}
ppd.ConnData.UpdateRecvAddress(ppd.LocalInitTime, ppd.ConnData.RemoteAddr)
peer.UpdateRecv(ppd.LocalInitTime)
peerAccepted:
}
ppd.RemotePubKey = peerPk
if ppd.ConnPeerPublicKey != nil {
copy((*ppd.ConnPeerPublicKey)[:], peerPk)
}View on GitHub (pinned to 6e04ca5ff0)