hashicorp/nomad · warning
handshake auth token mismatched auth header token
Error message
handshake auth token mismatched auth header token
What it means
When ws_handshake is enabled, the client must send its auth token both in the HTTP header and in the first handshake message. readWsHandshake rejects the connection when the header token and the handshake-message token differ, as a mismatch may indicate CSRF-style header injection from a browser or a compromised/misconfigured client.
Source
Thrown at command/agent/websockets.go:162
} else if h, err := strconv.ParseBool(hv); err != nil {
return "", fmt.Errorf("ws_handshake value is not a boolean: %v", err)
} else if !h {
return "", nil
}
// verify that any header token set by a non-browser client agrees with the
// auth header
reqToken := new(string)
s.parseToken(req, reqToken)
var h wsHandshakeMessage
err := readFn(&h)
if err != nil {
return "", err
}
if reqToken != nil && *reqToken != "" && *reqToken != h.AuthToken {
return "", fmt.Errorf("handshake auth token mismatched auth header token")
}
supportedWSHandshakeVersion := 1
if h.Version != supportedWSHandshakeVersion {
return "", fmt.Errorf("unexpected handshake value: %v", h.Version)
}
return h.AuthToken, nil
}
// getWebsocketConnection retrieves the websocket connection from context
func (s *HTTPServer) getWebsocketConnection(req *http.Request) (*websocket.Conn, error) {
ctx := req.Context()
// Get websocket connection from context (set by audit wrapper)
connRaw := ctx.Value(ctxKeyWebSocketConn)
if connRaw == nil {
return nil, fmt.Errorf("websocket connection not found in context")View on GitHub (pinned to 482b49bf1a)
Solutions
- Send the identical auth token in both the HTTP auth header and the websocket handshake message.
- Refresh the client so both tokens derive from the same current ACL token.
- Clear cached pages/scripts holding stale tokens and reload the UI.
- If a proxy manipulates headers, configure it to pass the auth header through unchanged.
Example fix
// before
msg := handshakeMessage{AuthToken: oldToken}
// after
msg := handshakeMessage{AuthToken: currentACLToken} // same value as auth header Defensive patterns
Strategy: validation
Validate before calling
if headerToken != "" && handshakeToken != headerToken {
return fmt.Errorf("handshake and header tokens must match")
} Try / catch
if err := readFn(&h); err != nil {
if strings.Contains(err.Error(), "mismatched auth header token") {
refreshTokenAndRetry()
}
return err
} Prevention
- Derive handshake token and header token from the same source at call time.
- Avoid caching tokens in long-lived browser pages; fetch per connection.
- Ensure proxies pass the auth header untouched.
- On mismatch, refresh the ACL token and reconnect once, not in a loop.
When it happens
Trigger: readFn decodes a handshake message whose AuthToken differs from the auth header token (reqToken) when reqToken is non-empty — e.g. a browser page that cannot set headers relaying a stale token.
Common situations: Browser-based exec where the handshake token was generated from an old ACL token while the page holds a newer header token; proxies that strip or rewrite auth headers; replayed handshake payloads.
Understand the failure class
- SSL/TLS and certificate errors — how TLS handshakes and certificate validation fail.
Related errors
- ACL token not found
- artifact includes symlink that resolves outside of sandbox
- no signed workload identity available
- JWT login returned an empty secret
- JWT login did not return a token
AI-assisted analysis of hashicorp/nomad@482b49bf1a (2026-09-04).
Data as JSON: /api/errors/3310eba97df53f5a.
Report an issue: GitHub.