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 one

View on GitHub (pinned to ad32144c42)

Solutions

  1. Re-fetch the node list and use a current node ID from /panel/api/nodes/list.
  2. If the page is stale, reload it so the cert-from-panel action references an existing node.
  3. Check the caller passes the inbound's NodeID (not the inbound ID) to this API.
  4. 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

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


AI-assisted analysis of MHSanaei/3x-ui@ad32144c42 (2026-08-15). Data as JSON: /api/errors/d70b5c982e603a68. Report an issue: GitHub.