goharbor/harbor · error
failed to parse the URL of registry
Error message
failed to parse the URL of registry: %v
What it means
Fatal initialization error in the registry reverse proxy setup: the registry URL returned by config.RegistryURL() cannot be parsed by url.Parse, so the single-host reverse proxy for /v2/ traffic cannot be constructed. The package-level proxy is built via init-time newProxy(), so this panic aborts process startup when the registry URL is malformed.
Solutions
- Set a valid registry URL (e.g. http://registry:5000) in the Harbor configuration that feeds config.RegistryURL()
- Check the panic's underlying parse error for the exact defect (missing scheme, bad host, whitespace)
- Restart the affected core/portal component after fixing the configuration
Defensive patterns
Strategy: try-catch
When it happens
Trigger: Thrown at src/server/registry/proxy.go:33 when the library encounters an invalid state.
Common situations: See trigger scenarios.
Understand the failure class
- Parsing and encoding errors: unexpected token, malformed input — why parsers reject input and how to find the real culprit.
AI-assisted analysis of goharbor/harbor@7b2fd08cc5 (2026-08-16).
Data as JSON: /api/errors/bf18e5aef8460865.
Report an issue: GitHub.
Appendix: source
Thrown at src/server/registry/proxy.go:33
package registry
import (
"fmt"
"net/http"
"net/http/httputil"
"net/url"
commonhttp "github.com/goharbor/harbor/src/common/http"
"github.com/goharbor/harbor/src/lib/config"
)
var proxy = newProxy()
func newProxy() http.Handler {
regURL, _ := config.RegistryURL()
url, err := url.Parse(regURL)
if err != nil {
panic(fmt.Sprintf("failed to parse the URL of registry: %v", err))
}
proxy := httputil.NewSingleHostReverseProxy(url)
// The reverse proxy forwards all /v2/ traffic to a single host (the registry),
// so it deserves a dedicated transport with a large per-host idle connection
// pool. Relying on http.DefaultTransport (MaxIdleConnsPerHost=2) closes almost
// every connection after use under high concurrency, piling up TIME_WAIT
// sockets and eventually exhausting ephemeral ports (EADDRNOTAVAIL).
opts := []func(*http.Transport){
func(tr *http.Transport) {
tr.MaxIdleConns = 1024
tr.MaxIdleConnsPerHost = 1024
},
}
if commonhttp.InternalTLSEnabled() {
opts = append(opts, commonhttp.WithInternalTLSConfig())
}
proxy.Transport = commonhttp.NewTransport(opts...)
View on GitHub (pinned to 7b2fd08cc5)