{"record":{"id":"ac3882fab84e4f88","repo":"hashicorp/nomad","slug":"ws-handshake-value-is-not-a-boolean-v","errorCode":null,"errorMessage":"ws_handshake value is not a boolean: %v","messagePattern":"ws_handshake value is not a boolean: (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"command/agent/websockets.go","lineNumber":145,"sourceCode":"\t\tconn.WriteMessage(websocket.CloseMessage,\n\t\t\twebsocket.FormatCloseMessage(websocket.CloseNormalClosure, \"request complete\"))\n\n\t\treturn nil, nil\n\t}\n}\n\ntype wsHandshakeMessage struct {\n\tVersion   int    `json:\"version\"`\n\tAuthToken string `json:\"auth_token\"`\n}\n\n// readWsHandshake reads the websocket handshake message and returns the auth token\nfunc (s *HTTPServer) readWsHandshake(readFn func(any) error, req *http.Request) (string, error) {\n\t// Avoid handshake if request doesn't require one\n\tif hv := req.URL.Query().Get(\"ws_handshake\"); hv == \"\" {\n\t\treturn \"\", nil\n\t} else if h, err := strconv.ParseBool(hv); err != nil {\n\t\treturn \"\", fmt.Errorf(\"ws_handshake value is not a boolean: %v\", err)\n\t} else if !h {\n\t\treturn \"\", nil\n\t}\n\n\t// verify that any header token set by a non-browser client agrees with the\n\t// auth header\n\treqToken := new(string)\n\ts.parseToken(req, reqToken)\n\n\tvar h wsHandshakeMessage\n\terr := readFn(&h)\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\n\tif reqToken != nil && *reqToken != \"\" && *reqToken != h.AuthToken {\n\t\treturn \"\", fmt.Errorf(\"handshake auth token mismatched auth header token\")\n\t}","sourceCodeStart":127,"sourceCodeEnd":163,"githubUrl":"https://github.com/hashicorp/nomad/blob/482b49bf1aec006f089bcfc7e632d8f6ac303e5e/command/agent/websockets.go#L127-L163","documentation":"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.","triggerScenarios":"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).","commonSituations":"Hand-rolled websocket clients or templates interpolating strings into the query string; proxies rewriting boolean query values; users typing 'yes'/'on' by intuition.","solutions":["Send ws_handshake=true or ws_handshake=false (or 1/0, t/f) — exactly the literals strconv.ParseBool accepts.","Omit the ws_handshake parameter entirely when no handshake is needed (empty means skip).","Fix the client code constructing the URL to pass a real boolean, not a string.","Update wrapper scripts/SDKs that interpolate malformed query values."],"exampleFix":"// before\nurl := base + \"/exec?ws_handshake=yes\"\n// after\nurl := base + \"/exec?ws_handshake=true\"","handlingStrategy":"validation","validationCode":"hv := req.URL.Query().Get(\"ws_handshake\")\nif hv != \"\" {\n    if _, err := strconv.ParseBool(hv); err != nil {\n        http.Error(w, \"ws_handshake must be a boolean\", http.StatusBadRequest)\n        return\n    }\n}","typeGuard":null,"tryCatchPattern":"if _, err := strconv.ParseBool(hv); err != nil {\n    http.Error(w, fmt.Sprintf(\"ws_handshake value is not a boolean: %v\", err), http.StatusBadRequest)\n    return\n}","preventionTips":["Always serialize booleans with strconv.FormatBool when building URLs.","Validate query params on the client before dispatching the request.","Keep SDK/wrapper templates typed (bool), not string-interpolated.","Document accepted literals (true/false/1/0/t/f)."],"tags":["websocket","query-param","validation","go"],"backgroundTag":"invalid-query-parameter","analyzedSha":"482b49bf1aec006f089bcfc7e632d8f6ac303e5e","analyzedAt":"2026-09-04T07:54:14.808Z","contentChangedAt":"2026-09-04T07:54:14.808Z","schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}