hashicorp/nomad · error

invalid redirect location %q: %w

Error message

invalid redirect location %q: %w

What it means

In api/api.go:1007, when a websocket dial receives a redirect status (301/307/308), the client parses the Location header and retries the websocket at the new path. If Location cannot be parsed as a URL, it returns this error wrapping the url.Parse failure. Typically means the server or an intermediary emitted a malformed or empty Location header.

Source

Thrown at api/api.go:1007

		wsScheme = "wss"
	default:
		return nil, nil, fmt.Errorf("unsupported scheme: %v", rhttp.URL.Scheme)
	}
	rhttp.URL.Scheme = wsScheme

	conn, resp, err := dialer.Dial(rhttp.URL.String(), rhttp.Header)

	// check resp status code, as it's more informative than handshake error we get from ws library
	if resp != nil {
		switch resp.StatusCode {
		case http.StatusSwitchingProtocols:
			// Connection upgrade was successful.

		case http.StatusPermanentRedirect, http.StatusTemporaryRedirect, http.StatusMovedPermanently:
			loc := resp.Header.Get("Location")
			u, err := url.Parse(loc)
			if err != nil {
				return nil, nil, fmt.Errorf("invalid redirect location %q: %w", loc, err)
			}
			return c.websocket(u.Path, q)

		default:
			var buf bytes.Buffer

			if resp.Header.Get("Content-Encoding") == "gzip" {
				greader, err := gzip.NewReader(resp.Body)
				if err != nil {
					return nil, nil, newUnexpectedResponseError(
						fromStatusCode(resp.StatusCode),
						withExpectedStatuses([]int{http.StatusSwitchingProtocols}),
						withError(err))
				}
				_, _ = io.Copy(&buf, greader)
			} else {
				_, _ = io.Copy(&buf, resp.Body)
			}

View on GitHub (pinned to 482b49bf1a)

Solutions

  1. curl -v the endpoint and inspect the raw Location header on the 3xx response
  2. Fix the redirecting proxy/server to emit a valid absolute or path-absolute Location
  3. Bypass the misbehaving intermediary and point the client directly at the Nomad agent/node address
  4. If the redirect targets another region/node, ensure its address is correct and properly URL-encoded

Example fix

// before: LB returns "Location: <nul>"
conn, _, err := c.websocket(path, q)
// after: validate and redirect explicitly yourself
resp, _ := http.Get(baseURL + path)
loc := resp.Header.Get("Location")
if _, perr := url.Parse(loc); perr != nil { return fmt.Errorf("upstream %s returned bad Location %q", baseURL, loc) }
conn, _, err := c.websocket(path, q)
Defensive patterns

Strategy: validation

Validate before calling

resp, err := http.Head(strings.TrimSuffix(baseURL, "/") + path)
if err == nil && resp.StatusCode >= 300 && resp.StatusCode < 400 {
	if _, err := url.Parse(resp.Header.Get("Location")); err != nil {
		return fmt.Errorf("upstream emits invalid redirect Location %q", resp.Header.Get("Location"))
	}
}

Try / catch

conn, _, err := c.websocket(path, q)
if err != nil && strings.Contains(err.Error(), "invalid redirect location") {
	return fmt.Errorf("redirecting intermediary is misconfigured; connect directly to the node: %w", err)
}

Prevention

When it happens

Trigger: Dialing a websocket (exec/logs) that gets answered with 301/307/308 whose Location header is missing or not a valid URL — commonly a badly configured proxy redirect or a redirect to a non-absolute/pct-encoded-broken target.

Common situations: Load balancers issuing redirects without Location; app-level handlers writing Location with invalid characters (raw spaces, control chars); redirect chains through proxies that mangle the header.

Related errors


AI-assisted analysis of hashicorp/nomad@482b49bf1a (2026-09-04). Data as JSON: /api/errors/7869e4383d1c947e. Report an issue: GitHub.