{"record":{"id":"2f0dd8f2aa6d374d","repo":"juanfont/headscale","slug":"node-not-found-2f0dd8","errorCode":null,"errorMessage":"node not found","messagePattern":"node not found","errorType":"http","errorClass":"ErrNodeNotFound","httpStatus":404,"severity":"error","filePath":"hscontrol/state/state.go","lineNumber":69,"sourceCode":"\t// TTL and a stripped-down RegistrationData payload (~200 bytes per entry),\n\t// 1024 entries cap the worst-case cache footprint at well under 1 MiB even\n\t// under sustained unauthenticated cache-fill attempts.\n\tdefaultRegisterCacheMaxEntries = 1024\n\n\t// defaultNodeStoreBatchSize is the default number of write operations to batch\n\t// before rebuilding the in-memory node snapshot.\n\tdefaultNodeStoreBatchSize = 100\n\n\t// defaultNodeStoreBatchTimeout is the default maximum time to wait before\n\t// processing a partial batch of node operations.\n\tdefaultNodeStoreBatchTimeout = 500 * time.Millisecond\n)\n\n// ErrUnsupportedPolicyMode is returned for invalid policy modes. Valid modes are \"file\" and \"db\".\nvar ErrUnsupportedPolicyMode = errors.New(\"unsupported policy mode\")\n\n// ErrNodeNotFound is returned when a node cannot be found by its ID.\nvar ErrNodeNotFound = errors.New(\"node not found\")\n\n// ErrInvalidNodeView is returned when an invalid node view is provided.\nvar ErrInvalidNodeView = errors.New(\"invalid node view provided\")\n\n// ErrNodeNotInNodeStore is returned when a node no longer exists in the [NodeStore].\nvar ErrNodeNotInNodeStore = errors.New(\"node no longer exists in NodeStore\")\n\n// ErrNodeNameNotUnique is returned when a node name is not unique.\nvar ErrNodeNameNotUnique = errors.New(\"node name is not unique\")\n\n// nodeUpdateColumns lists all Node columns that should be written\n// during a struct-based GORM Updates() call.  Listing them explicitly\n// forces GORM to include nil/zero-value fields (e.g. UserID=nil when\n// converting a user-owned node to tagged) that struct-based Updates()\n// would otherwise silently skip.\n//\n// Excluded columns:\n//   - AuthKeyID, AuthKey: prevents GORM from persisting stale","sourceCodeStart":51,"sourceCodeEnd":87,"githubUrl":"https://github.com/juanfont/headscale/blob/565fd254d06c4c7f9a8cad1714a43445c79ba420/hscontrol/state/state.go#L51-L87","documentation":"ErrNodeNotFound is a sentinel error in hscontrol/state/state.go:69 signalling that no node with the requested node ID exists. It is returned wrapped with the ID (e.g. state.go:703 fmt.Errorf(\"%w: %d\", ErrNodeNotFound, id)) from State lookups and from NodeStore.SetGivenName (hscontrol/state/node_store.go:484). API layers map it to HTTP 404 (hscontrol/api/v1/errors.go:23, v2/errors.go:70).","triggerScenarios":"Calling State methods such as GetNode/UpdateNode with an ID that was never allocated or was deleted; renaming a node via SetGivenName after it was deleted concurrently; gRPC/API requests referencing a stale node ID from an older listing.","commonSituations":"Node was deleted (via UI or 'headscale node delete') while a client or script still holds its ID; race between listing nodes and operating on one; using IDs from a different headscale instance/database.","solutions":["Re-list nodes (headscale node list or GET /api/v1/node) to confirm the ID still exists","If the node was deleted, re-register it or remove the stale ID from your automation","Check for concurrent deletions by other admins or cleanup jobs","Handle the 404 in your API client instead of retrying blindly"],"exampleFix":"null","handlingStrategy":"try-catch","validationCode":"node, err := st.GetNodeByID(ctx, id)\nif err != nil { /* check before mutating */ }","typeGuard":"null","tryCatchPattern":"if err != nil {\n    if errors.Is(err, state.ErrNodeNotFound) {\n        // 404: drop the stale reference, do not retry\n    }\n}","preventionTips":["Re-check node existence immediately before mutating operations","In automation, handle 404 as a signal to refresh the node list rather than retrying the same ID"],"tags":["headscale","node","not-found","api"],"backgroundTag":null,"analyzedSha":"565fd254d06c4c7f9a8cad1714a43445c79ba420","analyzedAt":"2026-08-15T13:12:30.133Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}