gohugoio/hugo · error
unable to obtain a valid tcp port: %v
Error message
unable to obtain a valid tcp port: %v
What it means
helpers.TCPListen asks the OS for an ephemeral port by listening on ":0", then type-asserts the returned net.Addr to *net.TCPAddr to report which port was assigned. This error means the listener was created but its address was not a *net.TCPAddr — an essentially pathological condition indicating something unusual about the network stack or an overridden net implementation, since a TCP listen should always yield a TCPAddr.
Source
Thrown at helpers/general.go:48
"github.com/jdkato/prose/transform"
)
// FilePathSeparator as defined by os.Separator.
const FilePathSeparator = string(filepath.Separator)
// TCPListen starts listening on a valid TCP port.
func TCPListen() (net.Listener, *net.TCPAddr, error) {
l, err := net.Listen("tcp", ":0")
if err != nil {
return nil, nil, err
}
addr := l.Addr()
if a, ok := addr.(*net.TCPAddr); ok {
return l, a, nil
}
l.Close()
return nil, nil, fmt.Errorf("unable to obtain a valid tcp port: %v", addr)
}
// FirstUpper returns a string with the first character as upper case.
func FirstUpper(s string) string {
if s == "" {
return ""
}
r, n := utf8.DecodeRuneInString(s)
return string(unicode.ToUpper(r)) + s[n:]
}
// ReaderToBytes takes an io.Reader argument, reads from it
// and returns bytes.
func ReaderToBytes(lines io.Reader) []byte {
if lines == nil {
return []byte{}
}
b := bp.GetBuffer()View on GitHub ↗ (pinned to 8a468df065)
Solutions
- Treat it as an environment problem: rerun outside the sandbox/container network shim, or on the host network (`docker run --network host`) to rule out proxy layers.
- Specify an explicit free port with `hugo server --port 1313` (and `--liveReloadPort`) to avoid the ephemeral-port path.
- Check for tools intercepting sockets (VPN clients, antivirus, syscall filters) and disable them for the dev server.
- If reproducible on a stock OS with a stock Hugo binary, report it upstream with OS and network details — it indicates a Go net anomaly.
Defensive patterns
Strategy: retry
Validate before calling
// Probe that an ephemeral port can be bound before starting the server
l, err := net.Listen("tcp", "127.0.0.1:0")
if err != nil { return fmt.Errorf("no free tcp port: %w", err) }
l.Close() Try / catch
for i := 0; i < 3; i++ {
err = startServer()
if err == nil || !strings.Contains(err.Error(), "valid tcp port") { break }
time.Sleep(time.Duration(i+1) * 500 * time.Millisecond)
} Prevention
- Pass an explicit free port with `--port` instead of relying on auto-selection
- Kill orphaned dev servers holding ports before relaunching (check with lsof/ss)
- In sandboxed environments, ensure the sandbox permits binding loopback TCP ports
When it happens
Trigger: Called when `hugo server` needs to auto-pick a free port (e.g. the requested port is taken, or for the LiveReload listener). The failure path requires net.Listen("tcp", ":0") to succeed but l.Addr() to return a non-TCPAddr, which does not happen with the standard Go runtime on normal systems.
Common situations: Exotic or sandboxed environments with nonstandard network namespaces, unusual socket proxying/LD_PRELOAD shims, or heavily restricted seccomp profiles; in practice users far more often see the plain net.Listen error (returned earlier, unwrapped) from firewalls or exhausted ports rather than this specific message.
Related errors
- failed to get remote ref %q: %w
- stopped after 10 redirects
- failed to fetch remote resource from '%s': %s
- failed to read remote resource %q: %w
- retry timeout (configured to %s) fetching remote resource: %
AI-assisted analysis of gohugoio/hugo@8a468df065 (2026-07-31).
Data as JSON: /data/errors/84290317d8d4e31f.json.
Report an issue: GitHub ↗.