gorilla/websocket · error
malformed ws or wss URL
Error message
malformed ws or wss URL
What it means
errMalformedURL is returned by DialContext when the URL string passed to Dial/DialContext cannot be parsed or does not use the ws:// or wss:// scheme. DialContext parses the URL with url.Parse and then validates the scheme before connecting.
Source
Thrown at client.go:135
// EnableCompression specifies if the client should attempt to negotiate
// per message compression (RFC 7692). Setting this value to true does not
// guarantee that compression will be supported. Currently only "no context
// takeover" modes are supported.
EnableCompression bool
// Jar specifies the cookie jar.
// If Jar is nil, cookies are not sent in requests and ignored
// in responses.
Jar http.CookieJar
}
// Dial creates a new client connection by calling DialContext with a background context.
func (d *Dialer) Dial(urlStr string, requestHeader http.Header) (*Conn, *http.Response, error) {
return d.DialContext(context.Background(), urlStr, requestHeader)
}
var errMalformedURL = errors.New("malformed ws or wss URL")
func hostPortNoPort(u *url.URL) (hostPort, hostNoPort string) {
hostPort = u.Host
hostNoPort = u.Host
if i := strings.LastIndex(u.Host, ":"); i > strings.LastIndex(u.Host, "]") {
hostNoPort = hostNoPort[:i]
} else {
switch u.Scheme {
case "wss":
hostPort += ":443"
case "https":
hostPort += ":443"
default:
hostPort += ":80"
}
}
return hostPort, hostNoPort
}View on GitHub (pinned to e064f32e36)
Solutions
- Ensure the URL starts with ws:// or wss:// and parses with url.Parse
- Prefer ws+tls-free ws:// for plaintext and wss:// for TLS; never use http(s) scheme strings
- Validate the scheme in config-loading code before constructing the dialer
- Use url.Parse yourself and check err and u.Scheme to surface a clearer message
Example fix
// before
u := os.Getenv("WS_URL") // e.g. "example.com/ws"
conn, _, err := dialer.Dial(u, nil)
// after
u := os.Getenv("WS_URL")
if !strings.HasPrefix(u, "ws://") && !strings.HasPrefix(u, "wss://") {
u = "wss://" + u
}
conn, _, err := dialer.Dial(u, nil) Defensive patterns
Strategy: validation
Validate before calling
func validateWSURL(raw string) error {
u, err := url.Parse(raw)
if err != nil {
return err
}
if u.Scheme != "ws" && u.Scheme != "wss" {
return fmt.Errorf("scheme must be ws or wss, got %q", u.Scheme)
}
if u.Host == "" {
return errors.New("missing host")
}
return nil
} Type guard
func isWSURL(raw string) bool {
u, err := url.Parse(raw)
return err == nil && (u.Scheme == "ws" || u.Scheme == "wss") && u.Host != ""
} Try / catch
if err := validateWSURL(cfg.WSURL); err != nil {
return fmt.Errorf("config error: %w", err)
}
conn, _, err := dialer.DialContext(ctx, cfg.WSURL, nil)
if err != nil {
return err
} Prevention
- Normalize config URLs at load time, adding wss:// if the scheme is missing
- Never build websocket URLs with http:// or https:// scheme strings
- Add a startup-time self-check that parses and validates configured endpoints
When it happens
Trigger: Passing a URL string to Dialer.Dial/DialContext that fails url.Parse, or one whose scheme is not "ws" or "wss" (e.g. http://, https://, or no scheme at all).
Common situations: Building the URL with the http scheme by habit, forgetting the scheme when concatenating host strings, environment variables containing bare hostnames ("example.com/socket" instead of "wss://example.com/socket"), or typos like ws:/ or wss//.
Understand the failure class
- Parsing and encoding errors: unexpected token, malformed input — why parsers reject input and how to find the real culprit.
Related errors
- websocket: bad write message type
- websocket: invalid compression level
- websocket: bad handshake
- websocket: invalid compression negotiation
- websocket: duplicate header not allowed:
AI-assisted analysis of gorilla/websocket@e064f32e36 (2026-08-31).
Data as JSON: /api/errors/8a81bda25b51fa45.
Report an issue: GitHub.