{"record":{"id":"741a5e9237b46dda","repo":"caddyserver/caddy","slug":"local-address-must-be-a-single-address-not-a-port","errorCode":null,"errorMessage":"local_address must be a single address, not a port range","messagePattern":"local_address must be a single address, not a port range","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"modules/caddyhttp/reverseproxy/httptransport.go","lineNumber":239,"sourceCode":"\n\t// Set a relatively short default dial timeout.\n\t// This is helpful to make load-balancer retries more speedy.\n\tif h.DialTimeout == 0 {\n\t\th.DialTimeout = caddy.Duration(3 * time.Second)\n\t}\n\n\tdialer := &net.Dialer{\n\t\tTimeout:       time.Duration(h.DialTimeout),\n\t\tFallbackDelay: time.Duration(h.FallbackDelay),\n\t}\n\n\tif h.LocalAddress != \"\" {\n\t\tnetaddr, err := caddy.ParseNetworkAddressWithDefaults(h.LocalAddress, \"tcp\", 0)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tif netaddr.PortRangeSize() > 1 {\n\t\t\treturn nil, fmt.Errorf(\"local_address must be a single address, not a port range\")\n\t\t}\n\t\tswitch netaddr.Network {\n\t\tcase \"tcp\", \"tcp4\", \"tcp6\":\n\t\t\tdialer.LocalAddr, err = net.ResolveTCPAddr(netaddr.Network, netaddr.JoinHostPort(0))\n\t\t\tif err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\tcase \"unix\", \"unixgram\", \"unixpacket\":\n\t\t\tdialer.LocalAddr, err = net.ResolveUnixAddr(netaddr.Network, netaddr.JoinHostPort(0))\n\t\t\tif err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\tcase \"udp\", \"udp4\", \"udp6\":\n\t\t\treturn nil, fmt.Errorf(\"local_address must be a TCP address, not a UDP address\")\n\t\tdefault:\n\t\t\treturn nil, fmt.Errorf(\"unsupported network\")\n\t\t}\n\t}","sourceCodeStart":221,"sourceCodeEnd":257,"githubUrl":"https://github.com/caddyserver/caddy/blob/50e54ee279aa1e504fe218ca49ab6ae16c100410/modules/caddyhttp/reverseproxy/httptransport.go#L221-L257","documentation":"When `local_address` is set on the HTTP transport, Caddy parses it with `caddy.ParseNetworkAddressWithDefaults` and requires the result to be a single port (`PortRangeSize() > 1` rejected), because the dialer needs exactly one local address to bind for outbound connections.","triggerScenarios":"Configuring `transport http { local_address host:8000-8009 }` or any address whose port expands to multiple ports (ranges, or a service name mapping to multiple ports).","commonSituations":"Copy-pasting an upstream-style address (where port ranges are valid for load balancing) into local_address; misunderstanding local_address as the bind range for a listening server rather than the source address of outgoing proxy connections.","solutions":["Change local_address to a single port or no port (port 0/omitted lets the OS pick an ephemeral port)","Remember local_address is the SOURCE address for connections to upstreams, not a listener"],"exampleFix":"# before\ntransport http {\n\tlocal_address 10.0.0.5:8000-8010\n}\n# after\ntransport http {\n\tlocal_address 10.0.0.5\n}","handlingStrategy":"validation","validationCode":"netaddr, err := caddy.ParseNetworkAddressWithDefaults(localAddr, \"tcp\", 0)\nif err != nil {\n\treturn err\n}\nif netaddr.PortRangeSize() > 1 {\n\treturn fmt.Errorf(\"local_address must be single port\")\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Remember local_address is the outbound source address, single-port only","Omit the port entirely to let the OS choose"],"tags":["reverse-proxy","http-transport","network","validation"],"backgroundTag":null,"analyzedSha":"50e54ee279aa1e504fe218ca49ab6ae16c100410","analyzedAt":"2026-08-15T09:20:21.641Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}