hashicorp/nomad · error

ws_handshake value is not a boolean: %v

Error message

ws_handshake value is not a boolean: %v

What it means

readWsHandshake parses the ws_handshake query parameter as a boolean to decide whether a websocket handshake message is required. This error is returned when the parameter is present but strconv.ParseBool cannot parse it, so the request is malformed before any upgrade.

Source

Thrown at command/agent/websockets.go:145

		conn.WriteMessage(websocket.CloseMessage,
			websocket.FormatCloseMessage(websocket.CloseNormalClosure, "request complete"))

		return nil, nil
	}
}

type wsHandshakeMessage struct {
	Version   int    `json:"version"`
	AuthToken string `json:"auth_token"`
}

// readWsHandshake reads the websocket handshake message and returns the auth token
func (s *HTTPServer) readWsHandshake(readFn func(any) error, req *http.Request) (string, error) {
	// Avoid handshake if request doesn't require one
	if hv := req.URL.Query().Get("ws_handshake"); hv == "" {
		return "", nil
	} 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")
	}

View on GitHub (pinned to 482b49bf1a)

Solutions

  1. Send ws_handshake=true or ws_handshake=false (or 1/0, t/f) — exactly the literals strconv.ParseBool accepts.
  2. Omit the ws_handshake parameter entirely when no handshake is needed (empty means skip).
  3. Fix the client code constructing the URL to pass a real boolean, not a string.
  4. Update wrapper scripts/SDKs that interpolate malformed query values.

Example fix

// before
url := base + "/exec?ws_handshake=yes"
// after
url := base + "/exec?ws_handshake=true"
Defensive patterns

Strategy: validation

Validate before calling

hv := req.URL.Query().Get("ws_handshake")
if hv != "" {
    if _, err := strconv.ParseBool(hv); err != nil {
        http.Error(w, "ws_handshake must be a boolean", http.StatusBadRequest)
        return
    }
}

Try / catch

if _, err := strconv.ParseBool(hv); err != nil {
    http.Error(w, fmt.Sprintf("ws_handshake value is not a boolean: %v", err), http.StatusBadRequest)
    return
}

Prevention

When it happens

Trigger: Client hits a websocket endpoint with e.g. ?ws_handshake=yes or ?ws_handshake=1.0 — any value outside strconv.ParseBool's accepted set (1, t, T, TRUE, true, True, 0, f, F, FALSE, false, False).

Common situations: Hand-rolled websocket clients or templates interpolating strings into the query string; proxies rewriting boolean query values; users typing 'yes'/'on' by intuition.

Understand the failure class

Background: "Invalid query parameter" / "Failed to parse value of ...": fixing bad query string parameters across APIs — this error's family across 36 libraries.

Related errors


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