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
- Open the node settings in the panel and set a valid port (1–65535, the sub-node panel port), then retry.
- If creating nodes via API, always include the port field and validate client-side before POSTing.
- 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
- Require the port field when creating/updating nodes via UI or API.
- Run a periodic audit query for nodes with port<=0 or port>65535.
- Fail fast at node-save time with a clear message instead of at first runtime call.
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.