MHSanaei/3x-ui · error
node not found
Error message
node not found
What it means
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).
Source
Thrown at internal/web/service/node.go:773
func (s *NodeService) SetEnable(id int, enable bool) error {
db := database.GetDB()
if err := db.Model(model.Node{}).Where("id = ?", id).Update("enable", enable).Error; err != nil {
return err
}
if mgr := runtime.GetManager(); mgr != nil {
mgr.InvalidateNode(id)
}
return nil
}
// GetWebCertFiles asks a node for its own web TLS certificate/key file paths,
// used by "Set Cert from Panel" so a node-assigned inbound gets paths that
// exist on the node rather than the central panel. See issue #4854.
func (s *NodeService) GetWebCertFiles(id int) (*runtime.WebCertFiles, error) {
n, err := s.GetById(id)
if err != nil || n == nil {
return nil, fmt.Errorf("node not found")
}
if !n.Enable {
return nil, fmt.Errorf("node is disabled")
}
mgr := runtime.GetManager()
if mgr == nil {
return nil, fmt.Errorf("runtime manager unavailable")
}
remote, err := mgr.RemoteFor(n)
if err != nil {
return nil, err
}
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
return remote.GetWebCertFiles(ctx)
}
// NodeUpdateResult reports the outcome of triggering a panel self-update on oneView on GitHub (pinned to ad32144c42)
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.
Example fix
// before: POST /panel/api/server/getWebCertFiles with id=99 (node deleted minutes ago) // err: node not found // after: reload node list, use the node's current id=101
Defensive patterns
Strategy: validation
Validate before calling
// Verify the node exists before calling cert/node RPCs
n, err := nodeSvc.GetById(id)
if err != nil || n == nil {
return fmt.Errorf("node %d does not exist; refresh the node list", id)
} Type guard
func isNodeNotFound(err error) bool {
return err != nil && strings.Contains(err.Error(), "node not found")
} Try / catch
files, err := nodeSvc.GetWebCertFiles(id)
if err != nil {
if isNodeNotFound(err) {
return nil, errNotFound("node", id) // map to a 404-style response, let UI refresh
}
return nil, err
} Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- node is disabled
- The new username and password are empty
- tg_id must be a positive integer
- token not found
- invalid metric
AI-assisted analysis of MHSanaei/3x-ui@ad32144c42 (2026-08-15).
Data as JSON: /api/errors/d70b5c982e603a68.
Report an issue: GitHub.