{"record":{"id":"c52253e361170aba","repo":"juanfont/headscale","slug":"handling-register-interactive-w","errorCode":null,"errorMessage":"handling register interactive: %w","messagePattern":"handling register interactive: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"hscontrol/auth.go","lineNumber":149,"sourceCode":"\t// logins as they can be done fully sync and we can respond to the node with\n\t// the result as it is waiting.\n\tif isAuthKey(req) {\n\t\tresp, err := h.handleRegisterWithAuthKey(req, machineKey)\n\t\tif err != nil {\n\t\t\t// Preserve HTTPError types so they can be handled properly by the HTTP layer\n\t\t\tif httpErr, ok := errors.AsType[HTTPError](err); ok {\n\t\t\t\treturn nil, httpErr\n\t\t\t}\n\n\t\t\treturn nil, fmt.Errorf(\"handling register with auth key: %w\", err)\n\t\t}\n\n\t\treturn resp, nil\n\t}\n\n\tresp, err := h.handleRegisterInteractive(req, machineKey)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"handling register interactive: %w\", err)\n\t}\n\n\treturn resp, nil\n}\n\n// handleLogout checks if the [tailcfg.RegisterRequest] is a\n// logout attempt from a node. If the node is not attempting to.\nfunc (h *Headscale) handleLogout(\n\tnode types.NodeView,\n\treq tailcfg.RegisterRequest,\n\tmachineKey key.MachinePublic,\n) (*tailcfg.RegisterResponse, error) {\n\t// Fail closed if it looks like this is an attempt to modify a node where\n\t// the node key and the machine key the noise session was started with does\n\t// not align.\n\terr := machineKeyMismatch(node, machineKey)\n\tif err != nil {\n\t\treturn nil, err","sourceCodeStart":131,"sourceCodeEnd":167,"githubUrl":"https://github.com/juanfont/headscale/blob/565fd254d06c4c7f9a8cad1714a43445c79ba420/hscontrol/auth.go#L131-L167","documentation":"Wraps failures of handleRegisterInteractive during interactive (browser-based) registration (hscontrol/auth.go:149). This path generates an AuthID and registers the node after the user completes the web/OIDC flow. Errors include the registration-ID generation failure (see 'generating registration ID') and any state/DB errors while creating or updating the pending registration and node record.","triggerScenarios":"`tailscale login` / `tailscale up` without an auth key: the node contacts the register endpoint, and creation of the interactive-registration record or subsequent node persistence fails on a database error; entropy source failure when generating the AuthID.","commonSituations":"Interactive enrollment during DB outages; SQLite lock contention when a browser callback races other writes; heavily loaded control servers with exhausted connection pools.","solutions":["Inspect the wrapped error in the headscale log — it names the actual failing step (ID generation vs DB write).","Restore database health (connectivity, locks, disk space) and retry the login from the node; interactive registration can simply be re-attempted.","As a workaround for automation, switch to pre-auth keys which bypass the interactive flow.","If caused by parallel interactive logins on SQLite, serialize enrollments or use PostgreSQL."],"exampleFix":null,"handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"resp, err := h.handleRegister(req, mk)\nif err != nil && strings.Contains(err.Error(), \"handling register interactive\") {\n    // re-attempt `tailscale login` once DB/entropy issue clears; or switch to preauth key flow\n}","preventionTips":["Use pre-auth keys for automation to avoid the interactive path entirely.","Keep DB connectivity healthy during interactive enrollments.","Alert on registration-endpoint 5xx rates."],"tags":["authentication","registration","interactive-login","database"],"backgroundTag":null,"analyzedSha":"565fd254d06c4c7f9a8cad1714a43445c79ba420","analyzedAt":"2026-08-15T13:12:30.133Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}