{"record":{"id":"dfe69975aefe50e8","repo":"MHSanaei/3x-ui","slug":"invalid-node-port-d","errorCode":null,"errorMessage":"invalid node port %d","messagePattern":"invalid node port (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/web/runtime/remote.go","lineNumber":163,"sourceCode":"\t\tif r.node.OutboundTag != \"\" && r.egressResolver != nil {\n\t\t\tproxyURL = r.egressResolver.NodeEgressProxyURL(r.node.Id)\n\t\t}\n\t\tr.client, r.clientErr = HTTPClientForNode(r.node, proxyURL)\n\t})\n\treturn r.client, r.clientErr\n}\n\nfunc (r *Remote) baseURL() (string, error) {\n\taddr, err := netsafe.NormalizeHost(r.node.Address)\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\tscheme := r.node.Scheme\n\tif scheme != \"http\" && scheme != \"https\" {\n\t\tscheme = \"https\"\n\t}\n\tif r.node.Port <= 0 || r.node.Port > 65535 {\n\t\treturn \"\", fmt.Errorf(\"invalid node port %d\", r.node.Port)\n\t}\n\tbp := r.node.BasePath\n\tif !strings.HasSuffix(bp, \"/\") {\n\t\tbp += \"/\"\n\t}\n\tu := &url.URL{\n\t\tScheme: scheme,\n\t\tHost:   net.JoinHostPort(addr, strconv.Itoa(r.node.Port)),\n\t\tPath:   bp,\n\t}\n\treturn u.String(), nil\n}\n\nfunc (r *Remote) do(ctx context.Context, method, path string, body any) (*envelope, error) {\n\t// mtls nodes authenticate via the client certificate, so a bearer token is\n\t// optional for them; every other mode still requires one.\n\tif r.node.ApiToken == \"\" && r.node.TlsVerifyMode != \"mtls\" {\n\t\treturn nil, errors.New(\"node has no API token configured\")","sourceCodeStart":145,"sourceCodeEnd":181,"githubUrl":"https://github.com/MHSanaei/3x-ui/blob/ad32144c42455696ea9f14e12168beac3e25f5d2/internal/web/runtime/remote.go#L145-L181","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"-- before\nINSERT INTO nodes (name, address, port) VALUES ('n1', '10.0.0.2', 0);\n\n-- after\nINSERT INTO nodes (name, address, port) VALUES ('n1', '10.0.0.2', 62789);","handlingStrategy":"validation","validationCode":"func validNodePort(p int) bool { return p > 0 && p <= 65535 }\n\nif !validNodePort(node.Port) {\n    return fmt.Errorf(\"node %d port %d out of range 1-65535\", node.Id, node.Port)\n}","typeGuard":"function isValidNodePort(p: unknown): p is number {\n  return typeof p === 'number' && Number.isInteger(p) && p > 0 && p <= 65535\n}","tryCatchPattern":null,"preventionTips":["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."],"tags":["nodes","validation","multi-node","configuration"],"backgroundTag":null,"analyzedSha":"ad32144c42455696ea9f14e12168beac3e25f5d2","analyzedAt":"2026-08-15T11:13:23.905Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}