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

  1. 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.
  2. Specify an explicit free port with `hugo server --port 1313` (and `--liveReloadPort`) to avoid the ephemeral-port path.
  3. Check for tools intercepting sockets (VPN clients, antivirus, syscall filters) and disable them for the dev server.
  4. 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

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


AI-assisted analysis of gohugoio/hugo@8a468df065 (2026-07-31). Data as JSON: /data/errors/84290317d8d4e31f.json. Report an issue: GitHub ↗.