{"record":{"id":"838bcbb1b2dd96f8","repo":"juanfont/headscale","slug":"registration-expired","errorCode":null,"errorMessage":"registration expired","messagePattern":"registration expired","errorType":"http","errorClass":"ErrRegistrationExpired","httpStatus":404,"severity":"error","filePath":"hscontrol/state/state.go","lineNumber":114,"sourceCode":"\t\"NodeKey\",\n\t\"DiscoKey\",\n\t\"Endpoints\",\n\t\"Hostinfo\",\n\t\"IPv4\",\n\t\"IPv6\",\n\t\"Hostname\",\n\t\"GivenName\",\n\t\"UserID\",\n\t\"RegisterMethod\",\n\t\"Tags\",\n\t\"Expiry\",\n\t\"LastSeen\",\n\t\"ApprovedRoutes\",\n\t\"UpdatedAt\",\n}\n\n// ErrRegistrationExpired is returned when a registration has expired.\nvar ErrRegistrationExpired = errors.New(\"registration expired\")\n\n// ErrNodeKeyInUse is returned when a registration or re-auth claims a NodeKey\n// already bound to a different machine, enforcing the 1:1 NodeKey<->MachineKey\n// binding.\nvar ErrNodeKeyInUse = errors.New(\"node key already in use by another machine\")\n\n// ErrAmbiguousNodeOwnership is returned when a machine key maps to a set of\n// nodes from which the correct one to update or convert cannot be determined:\n// multiple user-owned candidates for a tagged conversion, or a tagged node and\n// a user-owned node coexisting (impossible per validateNodeOwnership). The\n// registration is rejected rather than mutating an arbitrarily-picked node.\nvar ErrAmbiguousNodeOwnership = errors.New(\"machine key maps to ambiguous node ownership\")\n\n// sshCheckPair identifies a (source, destination) node pair for\n// SSH check auth tracking.\ntype sshCheckPair struct {\n\tSrc types.NodeID\n\tDst types.NodeID","sourceCodeStart":96,"sourceCodeEnd":132,"githubUrl":"https://github.com/juanfont/headscale/blob/565fd254d06c4c7f9a8cad1714a43445c79ba420/hscontrol/state/state.go#L96-L132","documentation":"ErrRegistrationExpired is a sentinel error in hscontrol/state/state.go:114 signalling that a node registration attempt timed out before an auth verdict arrived. The register cache resolves entries that time out or are evicted with this error (state.go:221 rn.FinishAuth(types.AuthVerdict{Err: ErrRegistrationExpired}); auth_cache_test.go confirms evicted entries surface it). API layers map it to a register-failed response (v1/errors.go:27).","triggerScenarios":"A node starts registration (interactive URL or pre-auth key flow) but no verdict arrives within tuning.register_cache_expiration; the cache entry is evicted (max entries exceeded) before the user completes auth; the OIDC callback or key validation never lands.","commonSituations":"User opens the registration URL but completes login after the timeout; heavy registration bursts exceeding register_cache_max_entries (default 1024) evicting pending entries; slow IdP or unreachable callback during OIDC registration.","solutions":["Re-run 'tailscale up/login' on the node to start a fresh registration","Complete the auth flow promptly after the registration URL is issued","Increase tuning.register_cache_expiration if auth legitimately takes longer (e.g. slow MFA)","Increase tuning.register_cache_max_entries under registration bursts"],"exampleFix":"null","handlingStrategy":"retry","validationCode":"null","typeGuard":"null","tryCatchPattern":"if errors.Is(err, state.ErrRegistrationExpired) {\n    // safe to retry: run tailscale up/login again for a fresh registration URL\n}","preventionTips":["Complete registration auth promptly after the URL is printed","Size tuning.register_cache_expiration / register_cache_max_entries to your MFA latency and registration volume"],"tags":["headscale","registration","auth","timeout","cache"],"backgroundTag":null,"analyzedSha":"565fd254d06c4c7f9a8cad1714a43445c79ba420","analyzedAt":"2026-08-15T13:12:30.133Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}