gofiber/fiber · warning
failed to resolve TCP address after adding port
Error message
failed to resolve TCP address after adding port: %w
What it means
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).
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.
Example fix
// before: hostname not resolvable by the backend r.RemoteAddr = "internal-svc:8080" // DNS only known to the proxy // after: use a literal IP the backend can resolve, or fix DNS r.RemoteAddr = net.JoinHostPort(realClientIP, "8080")
Defensive patterns
Strategy: validation
Validate before calling
// Before passing RemoteAddr to the adaptor, normalize it to a literal ip:port.
func normalizeRemoteAddr(raw string) (string, error) {
host, port, err := net.SplitHostPort(raw)
if err == nil && port != "" {
if ip := net.ParseIP(host); ip != nil {
return net.JoinHostPort(host, port), nil
}
}
// resolve hostname to IP first to avoid the resolver-retry path
ips, err := net.LookupHost(host)
if err != nil || len(ips) == 0 {
return "", fmt.Errorf("cannot resolve %q: %w", host, err)
}
if port == "" { port = "80" }
return net.JoinHostPort(ips[0], port), nil
} Try / catch
addr, err := resolveRemoteAddr(remote, local)
if err != nil {
if errors.Is(err, adaptor.ErrRemoteAddrTooLong) || strings.Contains(err.Error(), "failed to resolve TCP address") {
// fall back to a safe default RemoteAddr so the request still proceeds
addr = fallbackAddr
} else {
return err
}
} Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- failed to resolve TCP address
- remote address cannot be empty
- : lookup failed
- cache: failed to delete key
- cache: failed to get key
AI-assisted analysis of gofiber/fiber@a105acad6c (2026-08-11).
Data as JSON: /api/errors/f91f0ab2ab6a79ac.
Report an issue: GitHub.
Appendix: source
Thrown at middleware/adaptor/adaptor.go:571
return newTCPAddr(a[:], port, ip.Zone()), nil
}
}
}
resolved, err := net.ResolveTCPAddr("tcp", remoteAddr)
if err == nil {
return resolved, nil
}
var addrErr *net.AddrError
if errors.As(err, &addrErr) && addrErr != nil && addrErr.Err == "missing port in address" {
if len(remoteAddr) > 253 { // Max hostname length
return nil, ErrRemoteAddrTooLong
}
remoteAddr = net.JoinHostPort(remoteAddr, "80")
resolved, err2 := net.ResolveTCPAddr("tcp", remoteAddr)
if err2 != nil {
return nil, fmt.Errorf("failed to resolve TCP address after adding port: %w", err2)
}
return resolved, nil
}
return nil, fmt.Errorf("failed to resolve TCP address: %w", err)
}
func handlerFunc(app *fiber.App, h ...fiber.Handler) http.HandlerFunc {
// App.Config returns the config by value, so read the body limit once at
// construction instead of copying the whole 624-byte struct on every
// request. Fiber only writes app.config in New. The error handler is
// deliberately not cached: App.ErrorHandler resolves a mounted sub-app's
// handler from the request path, and that lookup belongs per request.
maxBodySize := int64(app.Config().BodyLimit)
return func(w http.ResponseWriter, r *http.Request) {
// New fasthttp Ctx from pool
pctx := ctxPool.Get().(*pooledCtx) //nolint:forcetypeassert,errcheck // not needed
fctx := &pctx.fctxView on GitHub (pinned to a105acad6c)