MHSanaei/3x-ui · error

invalid node port %d

Error message

invalid node port %d

What it means

Remote.baseURL (internal/web/runtime/remote.go) constructs the master↔sub-node API base URL from the node record: it normalizes the address, defaults the scheme to https unless http/https, then hard-fails with 'invalid node port %d' when node.Port is outside 1–65535. This guard runs before any network traffic, so a sub-node operation (cert fetch, sync, restart) fails fast on a bad node row.

Source

Thrown at internal/web/runtime/remote.go:163

		if r.node.OutboundTag != "" && r.egressResolver != nil {
			proxyURL = r.egressResolver.NodeEgressProxyURL(r.node.Id)
		}
		r.client, r.clientErr = HTTPClientForNode(r.node, proxyURL)
	})
	return r.client, r.clientErr
}

func (r *Remote) baseURL() (string, error) {
	addr, err := netsafe.NormalizeHost(r.node.Address)
	if err != nil {
		return "", err
	}
	scheme := r.node.Scheme
	if scheme != "http" && scheme != "https" {
		scheme = "https"
	}
	if r.node.Port <= 0 || r.node.Port > 65535 {
		return "", fmt.Errorf("invalid node port %d", r.node.Port)
	}
	bp := r.node.BasePath
	if !strings.HasSuffix(bp, "/") {
		bp += "/"
	}
	u := &url.URL{
		Scheme: scheme,
		Host:   net.JoinHostPort(addr, strconv.Itoa(r.node.Port)),
		Path:   bp,
	}
	return u.String(), nil
}

func (r *Remote) do(ctx context.Context, method, path string, body any) (*envelope, error) {
	// mtls nodes authenticate via the client certificate, so a bearer token is
	// optional for them; every other mode still requires one.
	if r.node.ApiToken == "" && r.node.TlsVerifyMode != "mtls" {
		return nil, errors.New("node has no API token configured")

View on GitHub (pinned to ad32144c42)

Solutions

  1. Open the node settings in the panel and set a valid port (1–65535, the sub-node panel port), then retry.
  2. If creating nodes via API, always include the port field and validate client-side before POSTing.
  3. Audit the nodes table for Port=0 rows: SELECT id, address, port FROM nodes WHERE port <= 0 OR port > 65535.

Example fix

-- before
INSERT INTO nodes (name, address, port) VALUES ('n1', '10.0.0.2', 0);

-- after
INSERT INTO nodes (name, address, port) VALUES ('n1', '10.0.0.2', 62789);
Defensive patterns

Strategy: validation

Validate before calling

func validNodePort(p int) bool { return p > 0 && p <= 65535 }

if !validNodePort(node.Port) {
    return fmt.Errorf("node %d port %d out of range 1-65535", node.Id, node.Port)
}

Type guard

function isValidNodePort(p: unknown): p is number {
  return typeof p === 'number' && Number.isInteger(p) && p > 0 && p <= 65535
}

Prevention

When it happens

Trigger: Any multi-node runtime call (node sync, node info fetch, panel update push) against a node whose Port column is 0 (unset), negative, or >65535 — e.g. a node created via API without a port, or a DB row edited by hand.

Common situations: Node added with port left at default 0; port stored as 0 because the form/API treated it as optional; integer overflow from importing configs; DB migrated between engines and column default lost.

Related errors


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