{"record":{"id":"d70b5c982e603a68","repo":"MHSanaei/3x-ui","slug":"node-not-found","errorCode":null,"errorMessage":"node not found","messagePattern":"node not found","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/web/service/node.go","lineNumber":773,"sourceCode":"\nfunc (s *NodeService) SetEnable(id int, enable bool) error {\n\tdb := database.GetDB()\n\tif err := db.Model(model.Node{}).Where(\"id = ?\", id).Update(\"enable\", enable).Error; err != nil {\n\t\treturn err\n\t}\n\tif mgr := runtime.GetManager(); mgr != nil {\n\t\tmgr.InvalidateNode(id)\n\t}\n\treturn nil\n}\n\n// GetWebCertFiles asks a node for its own web TLS certificate/key file paths,\n// used by \"Set Cert from Panel\" so a node-assigned inbound gets paths that\n// exist on the node rather than the central panel. See issue #4854.\nfunc (s *NodeService) GetWebCertFiles(id int) (*runtime.WebCertFiles, error) {\n\tn, err := s.GetById(id)\n\tif err != nil || n == nil {\n\t\treturn nil, fmt.Errorf(\"node not found\")\n\t}\n\tif !n.Enable {\n\t\treturn nil, fmt.Errorf(\"node is disabled\")\n\t}\n\tmgr := runtime.GetManager()\n\tif mgr == nil {\n\t\treturn nil, fmt.Errorf(\"runtime manager unavailable\")\n\t}\n\tremote, err := mgr.RemoteFor(n)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)\n\tdefer cancel()\n\treturn remote.GetWebCertFiles(ctx)\n}\n\n// NodeUpdateResult reports the outcome of triggering a panel self-update on one","sourceCodeStart":755,"sourceCodeEnd":791,"githubUrl":"https://github.com/MHSanaei/3x-ui/blob/ad32144c42455696ea9f14e12168beac3e25f5d2/internal/web/service/node.go#L755-L791","documentation":"Returned by NodeService.GetWebCertFiles when GetById(id) errors or returns nil — no node row exists with the requested ID. The message deliberately hides the underlying DB error, so it reads purely as 'unknown node'. Used by the 'Set Cert from Panel' flow for node-assigned inbounds (issue #4854).","triggerScenarios":"Calling GetWebCertFiles with a stale/deleted node ID — e.g. the node was removed after the inbound-edit page loaded, a hand-crafted API request with an arbitrary ID, or an ID confusion between node ID and inbound ID.","commonSituations":"UI page left open while another admin deleted the node; client sends inbound ID where the API expects node ID; node table restored from backup so IDs shifted.","solutions":["Re-fetch the node list and use a current node ID from /panel/api/nodes/list.","If the page is stale, reload it so the cert-from-panel action references an existing node.","Check the caller passes the inbound's NodeID (not the inbound ID) to this API.","If the DB was restored and IDs changed, re-assign inbounds to their nodes."],"exampleFix":"// before: POST /panel/api/server/getWebCertFiles with id=99 (node deleted minutes ago)\n// err: node not found\n\n// after: reload node list, use the node's current id=101","handlingStrategy":"validation","validationCode":"// Verify the node exists before calling cert/node RPCs\nn, err := nodeSvc.GetById(id)\nif err != nil || n == nil {\n    return fmt.Errorf(\"node %d does not exist; refresh the node list\", id)\n}","typeGuard":"func isNodeNotFound(err error) bool {\n    return err != nil && strings.Contains(err.Error(), \"node not found\")\n}","tryCatchPattern":"files, err := nodeSvc.GetWebCertFiles(id)\nif err != nil {\n    if isNodeNotFound(err) {\n        return nil, errNotFound(\"node\", id) // map to a 404-style response, let UI refresh\n    }\n    return nil, err\n}","preventionTips":["Pass the inbound's NodeID — not the inbound ID — to node-scoped APIs.","Refresh node lists after any node deletion before performing node-scoped actions in the UI.","After DB restores, audit node IDs referenced by inbounds."],"tags":["validation","node-management","api","stale-reference"],"backgroundTag":null,"analyzedSha":"ad32144c42455696ea9f14e12168beac3e25f5d2","analyzedAt":"2026-08-15T11:13:23.905Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}