{"record":{"id":"6f6f3d717dbd8d30","repo":"hashicorp/nomad","slug":"invalid-websocket-connection-in-context","errorCode":null,"errorMessage":"invalid websocket connection in context","messagePattern":"invalid websocket connection in context","errorType":"console","errorClass":null,"httpStatus":null,"severity":"error","filePath":"command/agent/websockets.go","lineNumber":184,"sourceCode":"\tif h.Version != supportedWSHandshakeVersion {\n\t\treturn \"\", fmt.Errorf(\"unexpected handshake value: %v\", h.Version)\n\t}\n\n\treturn h.AuthToken, nil\n}\n\n// getWebsocketConnection retrieves the websocket connection from context\nfunc (s *HTTPServer) getWebsocketConnection(req *http.Request) (*websocket.Conn, error) {\n\tctx := req.Context()\n\n\t// Get websocket connection from context (set by audit wrapper)\n\tconnRaw := ctx.Value(ctxKeyWebSocketConn)\n\tif connRaw == nil {\n\t\treturn nil, fmt.Errorf(\"websocket connection not found in context\")\n\t}\n\tconn, ok := connRaw.(*websocket.Conn)\n\tif !ok {\n\t\treturn nil, fmt.Errorf(\"invalid websocket connection in context\")\n\t}\n\n\treturn conn, nil\n}\n\n// runWebsocketWatcher reads messages from a websocket using the watcher\n// protocol. That usage does not read from the websocket, but reads are\n// required to receive control messages so this will continually read\n// the websocket until an error is encountered.\nfunc (s *HTTPServer) runWebsocketWatcher(conn *websocket.Conn) {\n\tfor {\n\t\tif _, _, err := conn.ReadMessage(); err != nil {\n\t\t\ts.logger.Trace(\"watcher websocket reader encountered error, stopping read\", \"error\", err)\n\t\t\treturn\n\t\t}\n\t}\n}\n","sourceCodeStart":166,"sourceCodeEnd":202,"githubUrl":"https://github.com/hashicorp/nomad/blob/482b49bf1aec006f089bcfc7e632d8f6ac303e5e/command/agent/websockets.go#L166-L202","documentation":"Type-guard failure in getWebsocketConnection: the request context lacks a *websocket.Conn under ctxKeyWebSocketConn (the value is missing or of the wrong type), meaning the audit wrapper did not attach an upgraded connection; callers allocExec/jobRunAction then abort.","triggerScenarios":"getWebsocketConnection (called by allocExec/jobRunAction) finds ctx.Value(ctxKeyWebSocketConn) non-nil but of a different concrete type — caused by another middleware storing a conflicting value under the same key or a refactor changing the stored type.","commonSituations":"Two wrappers using the same context key for different purposes; custom middleware patched in; tests injecting a mock connection of the wrong type into the request context.","solutions":["Fix the middleware responsible so it stores a *websocket.Conn under ctxKeyWebSocketConn.","Audit all uses of ctxKeyWebSocketConn for key collisions and consolidate on one setter.","Update tests/mocks to inject an actual *websocket.Conn (or the shared key with correct type).","Re-run against stock Nomad to rule out a fork/patched binary storing a wrong type."],"exampleFix":"// before\nctx = context.WithValue(ctx, ctxKeyWebSocketConn, myConnWrapper)\n// after\nctx = context.WithValue(ctx, ctxKeyWebSocketConn, wsConn) // *websocket.Conn","handlingStrategy":"type-guard","validationCode":"connRaw := ctx.Value(ctxKeyWebSocketConn)\nconn, ok := connRaw.(*websocket.Conn)\nif !ok {\n    return nil, fmt.Errorf(\"invalid websocket connection in context: %T\", connRaw)\n}","typeGuard":null,"tryCatchPattern":"conn, err := s.getWebsocketConnection(req)\nif err != nil {\n    log.Printf(\"ws context corruption (check middleware types): %v\", err)\n    return err\n}","preventionTips":["Use a single unexported key type for ctxKeyWebSocketConn.","Never store wrapper/mock types under that key.","Review custom middleware for context key collisions.","Include %T in error logs to identify the offending type fast."],"tags":["websocket","context","type-safety","go"],"backgroundTag":"invalid-context-type","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"}