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
- curl -v the endpoint and inspect the raw Location header on the 3xx response
- Fix the redirecting proxy/server to emit a valid absolute or path-absolute Location
- Bypass the misbehaving intermediary and point the client directly at the Nomad agent/node address
- 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
- Avoid proxies that redirect websocket upgrade requests
- Point the client directly at node HTTPAddr when following redirects fails
- Test redirect chains with curl -v before deploying
- Ensure Location headers are valid absolute or path-absolute URLs
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
- unable to unmarshal response with status %d: %v
- failed to send input: %w
- websocket closed before receiving exit code: %w
- unexpected HTTP transport: %T
- unsupported scheme: %v
AI-assisted analysis of hashicorp/nomad@482b49bf1a (2026-09-04).
Data as JSON: /api/errors/7869e4383d1c947e.
Report an issue: GitHub.