{"record":{"id":"37f25a0cf227e03b","repo":"juanfont/headscale","slug":"node-not-found","errorCode":null,"errorMessage":"node not found","messagePattern":"node not found","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"hscontrol/db/node.go","lineNumber":44,"sourceCode":"\n\t// defaultTestNodePrefix is the default hostname prefix for nodes created in tests.\n\tdefaultTestNodePrefix = \"testnode\"\n)\n\n// ErrNodeNameNotUnique is returned when a node name is not unique.\nvar ErrNodeNameNotUnique = errors.New(\"node name is not unique\")\n\n// preloadNode returns a session that eager-loads a node's AuthKey, the\n// AuthKey's User, and the node's User.\nfunc preloadNode(tx *gorm.DB) *gorm.DB {\n\treturn tx.\n\t\tPreload(\"AuthKey\").\n\t\tPreload(\"AuthKey.User\").\n\t\tPreload(\"User\")\n}\n\nvar (\n\tErrNodeNotFound                  = errors.New(\"node not found\")\n\tErrNodeRouteIsNotAvailable       = errors.New(\"route is not available on node\")\n\tErrNodeNotFoundRegistrationCache = errors.New(\n\t\t\"node not found in registration cache\",\n\t)\n\tErrCouldNotConvertNodeInterface = errors.New(\"failed to convert node interface\")\n)\n\n// ListPeers returns peers of node, regardless of any Policy or if the node is expired.\n// If no peer IDs are given, all peers are returned.\n// If at least one peer ID is given, only these peer nodes will be returned.\nfunc (hsdb *HSDatabase) ListPeers(nodeID types.NodeID, peerIDs ...types.NodeID) (types.Nodes, error) {\n\treturn ListPeers(hsdb.DB, nodeID, peerIDs...)\n}\n\n// ListPeers returns peers of node, regardless of any Policy or if the node is expired.\n// If no peer IDs are given, all peers are returned.\n// If at least one peer ID is given, only these peer nodes will be returned.\nfunc ListPeers(tx *gorm.DB, nodeID types.NodeID, peerIDs ...types.NodeID) (types.Nodes, error) {","sourceCodeStart":26,"sourceCodeEnd":62,"githubUrl":"https://github.com/juanfont/headscale/blob/565fd254d06c4c7f9a8cad1714a43445c79ba420/hscontrol/db/node.go#L26-L62","documentation":"Generic not-found sentinel in hscontrol/db/node.go returned by node lookup helpers (GetNode, and registration paths) when no row matches the requested node ID or machine key. It typically wraps or is wrapped with gorm.ErrRecordNotFound context at call sites.","triggerScenarios":"Calling GetNode/GetNodeByMachineKey/GetNodeByAnyKey with an ID that does not exist (deleted node, typo'd ID), or a registration cache miss during node re-registration (distinct sibling error ErrNodeNotFoundRegistrationCache).","commonSituations":"CLI command with a stale node ID after the node was deleted; API scripts holding cached IDs across node deletion; race between deletion and a concurrent query.","solutions":["Run `headscale nodes list` and re-check the identifier you are passing","If the node was deleted, re-register it with a fresh auth-key","In code, check errors.Is(err, db.ErrNodeNotFound) and treat it as a 404 path rather than a server error"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"node, err := hsdb.GetNodeByID(nodeID)\nif err != nil {\n    if errors.Is(err, db.ErrNodeNotFound) {\n        return http.NotFoundHandler().ServeHTTP // 404, not 500\n    }\n    return err\n}","preventionTips":["Treat node IDs as ephemeral: look them up by name/machine key when possible","Handle 404 explicitly in API clients and refresh cached IDs","Avoid racing deletions with reads by serializing admin operations"],"tags":["node","not-found","database","headscale","go"],"backgroundTag":null,"analyzedSha":"565fd254d06c4c7f9a8cad1714a43445c79ba420","analyzedAt":"2026-08-15T13:12:30.133Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}