{"record":{"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":571,"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":553,"sourceCodeEnd":589,"githubUrl":"https://github.com/gofiber/fiber/blob/a105acad6c1e4576a77f01e02973f67e962bb58d/middleware/adaptor/adaptor.go#L553-L589","documentation":"Returned by resolveRemoteAddr in the adaptor middleware when net.ResolveTCPAddr fails a second time after the function appended the default port ':80' to a host that was missing a port. This is the fallback path for hostnames or non-literal addresses passed as a remote address to Fiber's HTTP-handler-to-Fiber adaptor (used by net/http-based reverse proxies).","triggerScenarios":"A reverse proxy (httputil.ReverseProxy) forwards requests to the Fiber app via adaptor.FiberApp or adaptor.HTTPHandler with a Request.RemoteAddr whose host portion is a hostname that does not resolve in DNS, or contains invalid characters, or where the resolver is unavailable. The first ResolveTCPAddr reported 'missing port', the function appended ':80', and the retry still failed.","commonSituations":"Running behind a proxy that sets X-Forwarded-Host to a hostname the backend cannot resolve (split-horizon DNS, internal-only hostname); container where /etc/resolv.conf points at an unreachable resolver; the remote address was synthesized from a Host header containing brackets or spaces; localhost6/IPv6 literals that defeat the fast path and hit the resolver with a malformed string.","solutions":["Ensure the host portion of RemoteAddr is resolvable from the Fiber process: getent hosts <host> or nslookup <host>.","Fix /etc/resolv.conf or the container's DNS so the resolver is reachable.","If behind a proxy, set Request.RemoteAddr to a literal IP:port rather than a hostname before calling the adaptor.","Strip brackets/spaces from synthesized RemoteAddr values.","For IPv6, pass [ip]:port form so the fast path handles it without the resolver."],"exampleFix":"// before: hostname not resolvable by the backend\nr.RemoteAddr = \"internal-svc:8080\" // DNS only known to the proxy\n\n// after: use a literal IP the backend can resolve, or fix DNS\nr.RemoteAddr = net.JoinHostPort(realClientIP, \"8080\")","handlingStrategy":"validation","validationCode":"// Before passing RemoteAddr to the adaptor, normalize it to a literal ip:port.\nfunc normalizeRemoteAddr(raw string) (string, error) {\n    host, port, err := net.SplitHostPort(raw)\n    if err == nil && port != \"\" {\n        if ip := net.ParseIP(host); ip != nil {\n            return net.JoinHostPort(host, port), nil\n        }\n    }\n    // resolve hostname to IP first to avoid the resolver-retry path\n    ips, err := net.LookupHost(host)\n    if err != nil || len(ips) == 0 {\n        return \"\", fmt.Errorf(\"cannot resolve %q: %w\", host, err)\n    }\n    if port == \"\" { port = \"80\" }\n    return net.JoinHostPort(ips[0], port), nil\n}","typeGuard":null,"tryCatchPattern":"addr, err := resolveRemoteAddr(remote, local)\nif err != nil {\n    if errors.Is(err, adaptor.ErrRemoteAddrTooLong) || strings.Contains(err.Error(), \"failed to resolve TCP address\") {\n        // fall back to a safe default RemoteAddr so the request still proceeds\n        addr = fallbackAddr\n    } else {\n        return err\n    }\n}","preventionTips":["Always set Request.RemoteAddr to ip:port before invoking the adaptor.","If behind a proxy, parse X-Forwarded-For into a literal IP.","Keep /etc/resolv.conf reachable from the Fiber process.","For IPv6, use [v6]:port form to hit the adaptor's fast path."],"tags":["adaptor","network","dns","reverse-proxy","net-http"],"backgroundTag":null,"analyzedSha":"a105acad6c1e4576a77f01e02973f67e962bb58d","analyzedAt":"2026-08-11T17:33:26.942Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}