{"record":{"id":"38c3d3de6501c540","repo":"slackhq/nebula","slug":"erralreadyseen","errorCode":"ErrAlreadySeen","errorMessage":"already seen","messagePattern":"already seen","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"handshake_manager.go","lineNumber":415,"sourceCode":"\t\t// Add any calculated remotes, and trigger early handshake if one found\n\t\tdoTrigger = hm.lightHouse.addCalculatedRemotes(vpnAddr)\n\t}\n\n\tif doTrigger {\n\t\tselect {\n\t\tcase hm.trigger <- vpnAddr:\n\t\tdefault:\n\t\t}\n\t}\n\n\thm.Unlock()\n\thm.lightHouse.QueryServer(vpnAddr)\n\treturn hostinfo\n}\n\nvar (\n\tErrExistingHostInfo    = errors.New(\"existing hostinfo\")\n\tErrAlreadySeen         = errors.New(\"already seen\")\n\tErrLocalIndexCollision = errors.New(\"local index collision\")\n)\n\n// CheckAndComplete checks for any conflicts in the main and pending hostmap\n// before adding hostinfo to main. If err is nil, it was added. Otherwise err will be:\n//\n// ErrAlreadySeen if we already have an entry in the hostmap that has seen the\n// exact same handshake packet\n//\n// ErrExistingHostInfo if we already have an entry in the hostmap for this\n// VpnIp and the new handshake was older than the one we currently have\n//\n// ErrLocalIndexCollision if we already have an entry in the main or pending\n// hostmap for the hostinfo.localIndexId.\nfunc (hm *HandshakeManager) CheckAndComplete(hostinfo *HostInfo, handshakePacket uint8, f *Interface) (*HostInfo, error) {\n\thm.mainHostMap.Lock()\n\tdefer hm.mainHostMap.Unlock()\n\thm.Lock()","sourceCodeStart":397,"sourceCodeEnd":433,"githubUrl":"https://github.com/slackhq/nebula/blob/dd8f660c0ac37903ec4080ca4d3c861ba9342ceb/handshake_manager.go#L397-L433","documentation":"ErrAlreadySeen indicates a packet failed the replay window check: its message counter was already processed by this connection, so decryption is refused to prevent replay attacks. Declared in handshake_manager.go alongside the hostmap errors, it is returned by ConnectionState Decrypt/VerifyRelay when the replay filter rejects the counter.","triggerScenarios":"Calling Decrypt or VerifyRelay on connection_state.go:104/116 where the NaCl/decrypt verification reports the message counter was already seen (result == false under cs.decryptLock).","commonSituations":"Duplicate UDP packets delivered by the network; packet replay (possibly malicious); out-of-order retransmits at the tunnel layer; restoring old packet captures onto a live connection.","solutions":["Treat isolated occurrences as benign network duplication and drop the packet","If frequent, investigate path duplication (bonded links, redundant routing, virtual NICs) delivering every packet twice","Ensure counters are not reset out-of-band (e.g. reloading an old hostinfo snapshot) which would make new traffic look replayed"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"out, err := cs.Decrypt(b, out)\nif errors.Is(err, ErrAlreadySeen) {\n    // replayed or duplicate packet: drop silently\n    return\n}","preventionTips":["Expect duplicates on redundant/lossy networks and drop them quietly","Never reset or reload connection counters while a tunnel is live","Alert on replay rates far above baseline (possible replay attack)"],"tags":["replay-attack","crypto","packet-processing"],"backgroundTag":"packet-replay-detected","analyzedSha":"dd8f660c0ac37903ec4080ca4d3c861ba9342ceb","analyzedAt":"2026-09-03T11:13:55.444Z","contentChangedAt":"2026-09-03T11:13:55.444Z","schemaVersion":2},"datasetVersion":"2026-09-10T17:17:09.494Z"}