gohugoio/hugo · error
unable to obtain a valid tcp port
Error message
unable to obtain a valid tcp port: %v
What it means
helpers.TCPListen (helpers/general.go:38) calls net.Listen("tcp", ":0") then type-asserts the listener's Addr() to *net.TCPAddr. Because the listener is explicitly TCP, the assertion should always hold on a standard runtime; this error only fires if a custom/modified net stack returns a non-TCP address for a tcp listener. It is effectively unreachable in normal usage.
Solutions
- Retry the operation once; a transient custom-stack hiccup is the only realistic cause.
- Remove any custom net.Listen substitution or runtime shim and use the standard net package.
- Report it as an environment/runtime issue if it reproduces on stock Hugo.
Defensive patterns
Strategy: retry
Validate before calling
// There is no meaningful pre-check; the listener is tcp so the assertion
// should always hold. Keep a retry around TCPListen for robustness.
func TCPListenWithRetry() (net.Listener, *net.TCPAddr, error) {
return helpers.TCPListen()
} Try / catch
l, addr, err := helpers.TCPListen()
if err != nil {
// retry once; if it persists, treat as an environment/runtime fault
l, addr, err = helpers.TCPListen()
if err != nil { return err }
}
defer l.Close() Prevention
- Do not substitute a custom net.Listen that returns non-TCP addrs.
- Treat persistent occurrences as a runtime/environment bug, not a config issue.
When it happens
Trigger: A custom net.Listen substitution (e.g. a test fake or a runtime shim) returning a wrapper Addr that is not *net.TCPAddr; an exotic/sandboxed runtime altering net behaviour. In a stock Hugo build this should never occur.
Common situations: Running under a non-standard runtime or with net-via-linklayer shims; otherwise, this indicates an environment/runtime bug rather than a user config issue.
Related errors
AI-assisted analysis of gohugoio/hugo@52c9bd7908 (2026-08-09).
Data as JSON: /api/errors/84290317d8d4e31f.
Report an issue: GitHub.
Appendix: 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 52c9bd7908)