{"id":"f91f0ab2ab6a79ac","repo":"gofiber/fiber","slug":"failed-to-resolve-tcp-address-after-adding-port","errorCode":null,"errorMessage":"failed to resolve TCP address after adding port: %w","messagePattern":"failed to resolve TCP address after adding port: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"middleware/adaptor/adaptor.go","lineNumber":456,"sourceCode":"\t\t\t\treturn newTCPAddr(a[:], port, ip.Zone()), nil\n\t\t\t}\n\t\t}\n\t}\n\n\tresolved, err := net.ResolveTCPAddr(\"tcp\", remoteAddr)\n\tif err == nil {\n\t\treturn resolved, nil\n\t}\n\n\tvar addrErr *net.AddrError\n\tif errors.As(err, &addrErr) && addrErr != nil && addrErr.Err == \"missing port in address\" {\n\t\tif len(remoteAddr) > 253 { // Max hostname length\n\t\t\treturn nil, ErrRemoteAddrTooLong\n\t\t}\n\t\tremoteAddr = net.JoinHostPort(remoteAddr, \"80\")\n\t\tresolved, err2 := net.ResolveTCPAddr(\"tcp\", remoteAddr)\n\t\tif err2 != nil {\n\t\t\treturn nil, fmt.Errorf(\"failed to resolve TCP address after adding port: %w\", err2)\n\t\t}\n\t\treturn resolved, nil\n\t}\n\treturn nil, fmt.Errorf(\"failed to resolve TCP address: %w\", err)\n}\n\nfunc handlerFunc(app *fiber.App, h ...fiber.Handler) http.HandlerFunc {\n\t// App.Config returns the config by value, so read the body limit once at\n\t// construction instead of copying the whole 624-byte struct on every\n\t// request. Fiber only writes app.config in New. The error handler is\n\t// deliberately not cached: App.ErrorHandler resolves a mounted sub-app's\n\t// handler from the request path, and that lookup belongs per request.\n\tmaxBodySize := int64(app.Config().BodyLimit)\n\n\treturn func(w http.ResponseWriter, r *http.Request) {\n\t\t// New fasthttp Ctx from pool\n\t\tpctx := ctxPool.Get().(*pooledCtx) //nolint:forcetypeassert,errcheck // not needed\n\t\tfctx := &pctx.fctx","sourceCodeStart":438,"sourceCodeEnd":474,"githubUrl":"https://github.com/gofiber/fiber/blob/9a4c7e57fe0b080a04235d28a4b0d2b4b353d58c/middleware/adaptor/adaptor.go#L438-L474","documentation":"Returned by resolveRemoteAddr in adaptor.FiberHandler/http adapter when the upstream http.Server gave a RemoteAddr with no port, the code appended default port ':80', and the retry of net.ResolveTCPAddr still failed. This indicates the host portion itself is malformed or unresolvable, not merely missing a port. It is produced inside the per-request handler, so the error is logged and remoteAddr falls back to nil rather than crashing the request.","triggerScenarios":"Wrapping a fiber.App with adaptor.FiberApp and having a reverse proxy/proxy chain populate RemoteAddr with a bare hostname, an IP with garbage trailing characters, or a hostname longer than 253 chars that escaped the earlier guard. X-Forwarded-For values containing invalid IPs that get assigned to r.RemoteAddr by a misbehaving upstream also trigger it.","commonSituations":"Load balancer sends 'X-Forwarded-For' with a malformed value that a proxy set verbatim into RemoteAddr; IPv6 link-local address with zone but no port; testing with a stub http.Server whose RemoteAddr is a hostname only; upgrading a proxy that previously appended ':80' to no longer doing so.","solutions":["Inspect what r.RemoteAddr actually contains at the adapter boundary (log it once) and fix the upstream that sets it.","Configure your proxy/LB to always send host:port in RemoteAddr (nginx 'proxy_set_header' / ELB proxy protocol).","If you control the caller, pass net.JoinHostPort(host, '80') explicitly so ResolveTCPAddr never sees a portless address.","Strip invalid characters from X-Forwarded-For before it reaches the Go http stack, or use trusted proxy hop parsing."],"exampleFix":"// before: upstream hands a bare hostname, resolve fails\n// r.RemoteAddr == \"frontend.local\"\n\n// after: nginx always emits host:port\n//   proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;\n// and on the Go side, normalize defensively:\nhost, port, err := net.SplitHostPort(r.RemoteAddr)\nif err != nil {\n  r.RemoteAddr = net.JoinHostPort(strings.TrimSpace(r.RemoteAddr), \"80\")\n}","handlingStrategy":"validation","validationCode":"// Normalize an upstream RemoteAddr to host:port before passing into adaptor.\nfunc normalizeRemoteAddr(addr string) string {\n    if addr == \"\" {\n        return \"\"\n    }\n    if _, _, err := net.SplitHostPort(addr); err == nil {\n        return addr // already has port\n    }\n    // bare host or IP -> append default http port\n    return net.JoinHostPort(strings.TrimSpace(addr), \"80\")\n}","typeGuard":"null","tryCatchPattern":"null","preventionTips":["Configure upstream proxies to always send host:port in RemoteAddr.","Reject/drop malformed X-Forwarded-For at the trust boundary.","Use net.JoinHostPort instead of string concatenation to build addresses.","Log r.RemoteAddr once at the adapter edge to catch upstream regressions."],"tags":["adaptor","http","network","remote-addr","proxy"],"analyzedSha":"9a4c7e57fe0b080a04235d28a4b0d2b4b353d58c","analyzedAt":"2026-08-04T21:44:03.395Z","schemaVersion":2}