Troubleshooting
This section describes in more depth the details of how WebSocketClient works.
WebSocket Handshake
A WebSocket connection begins life as an ordinary HTTP request. The client asks the
server to change protocols, and a server willing to do so answers 101 Switching
Protocols; from that point the socket carries WebSocket frames rather than HTTP.
Connect performs this exchange for you, but it is worth knowing what it sends, what
it does with the answer, and where the answer is left for you to look at.
What Is Sent
Conga composes the upgrade request, and WebSocketClient supplies three things to put
in it: the path (the path from URL, plus a query string
built from the URL's own and from Params), the host,
and the headers.
The headers are assembled in this order:
Headers, as you have built it up withAddHeader,SetHeader, and friends.Sec-WebSocket-ExtensionsfromExtensionsandSec-WebSocket-ProtocolfromProtocol- both added only if you have not set that header yourself.Authorization, fromAuthandAuthTypeif they are set (overwriting anyAuthorizationheader you set directly), or from credentials embedded in the URL if they are not.HeaderSubstitutionis applied to the result, replacing delimited environment-variable references with their values.- Headers with empty values are dropped.
Conga adds the protocol's own mandatory headers - Upgrade, Connection,
Sec-WebSocket-Key, and Sec-WebSocket-Version - so you neither need to nor should
set those yourself.
What Comes Back
Connect then waits WaitTime milliseconds for a
single Conga event, and what arrives decides the outcome:
| Event | Meaning |
|---|---|
WSUpgrade |
The server upgraded, and Conga has already validated the response (AutoUpgrade is 1) |
WSResponse |
The server responded and it is yours to validate (AutoUpgrade is 0) |
HTTPHeader |
An ordinary HTTP response - a redirection, or a refusal |
Timeout |
Nothing arrived within WaitTime; Connect returns 100 'Conga connection timed out' |
Error |
Conga reported an error, which becomes the rc |
Closed |
The server closed the socket instead of answering; Connect returns 'Socket closed by server' |
On either of the first two, the response is parsed into
WSUpgradeResponse before your hook sees
it, and it stays there after Connect returns:
ws.Connect
0 Connected
ws.WSUpgradeResponse.(version status message)
HTTP/1.1 101 Switching Protocols
ws.WSUpgradeResponse.headers
upgrade websocket
connection Upgrade
sec-websocket-accept vKfjfb62aH5uFI4KM/un/ixCh3k=
date Sat, 05 Sep 2026 19:01:09 GMT
server Fly/ec1a4f957c (2026-08-31)
ws.WSUpgradeResponse.headers ws.GetHeader 'upgrade'
websocket
status is a number, headers is a 2-column matrix that
GetHeader will search for you when passed as its left
argument, and payload holds anything that followed the headers - normally empty.
WSUpgradeResponse is '' if the handshake never got as far as a response.
Vetting the Handshake Yourself
Even with AutoUpgrade left at 1, an OnWSUpgrade
hook gets to see the parsed response and can veto the connection by returning a
non-zero rc, which Connect returns as its own result. This is where to check that
the server agreed to what you asked for - a sub-protocol, most usefully, since a
server is free to ignore the request and speak its own dialect instead:
∇ (rc msg)←client OnUpgrade response
[1] ⍝ refuse the connection unless the server agreed to our sub-protocol
[2] (rc msg)←0 ''
[3] :If 'chat'≢response.headers client.GetHeader 'sec-websocket-protocol'
[4] (rc msg)←¯1 'server did not accept the chat sub-protocol'
[5] :EndIf
∇
ws.(Protocol OnWSUpgrade)←'chat' 'OnUpgrade'
Setting AutoUpgrade to 0 goes further: Conga hands over the response without
validating it, WebSocketClient calls your
OnWSResponse hook, and only if that returns
0 does it accept the upgrade. The hook is then responsible for whatever checking the
WSUpgrade path would have done for you, so leave AutoUpgrade at 1 unless you
have a specific reason not to.
Both hooks run on the thread that called Connect, and errors in them are trapped -
Connect returns ¯1 and a msg beginning 'Unexpected ' rather than suspending,
unless Debug is non-zero.
Redirections
A server that answers with 301, 302, 303, 307, or 308 arrives as an
HTTPHeader event, and Connect starts again against the Location header - up to
MaxRedirections times. Each hop is recorded
in Redirections as a namespace holding the URL
that was tried and the response it produced, so a connection that ended up somewhere
unexpected can be traced afterwards. A redirection without a Location header, or one
too many hops, ends the attempt.
Any other HTTP status is a refusal: Connect returns
¯1 'Unexpected server response: ...' with the status and message, and
HttpStatus,
HttpMessage, and
HttpHeaders hold the response for inspection.
When the Handshake Fails Quietly
Not every server that declines to upgrade says so in HTTP. Asking
echo.websocket.org for a sub-protocol it does not support, for example, gets no
response at all - the server simply closes the socket:
ws.Protocol←'chat'
ws.Connect
1119 Socket closed by server
and with a short WaitTime the same attempt ends as
100 'Conga connection timed out' instead, because Connect gave up before the close
arrived. Either result, with WSUpgradeResponse still '', points at the request
rather than at the network: a header the server dislikes, a sub-protocol or extension
it will not speak, or a path it does not serve WebSockets on.
When a Connection Fails
Connect reports a failure rather than signalling one, so a connection that did not
happen leaves you with a result to interpret and a set of status fields to read. The
fields are described in Status-related fields; this section is
about which of them to look at, and when.
Connected is the dependable test. Connect's rc
is 0 on success, but a handful of validation failures - a URL that cannot be parsed,
headers that cannot be interpreted - currently report the problem in msg while
leaving rc at 0:
ws.URL←'ftp://example.com'
ws.Connect
0 Invalid protocol: ftp
ws.Connected
0
So test Connected (or check that msg is 'Connected') rather than testing rc
alone.
Reading the Message
Failures fall into a few groups, and the message says which:
| Message | What went wrong |
|---|---|
'No URL specified''URL is not a simple character vector''Headers are not character''Improper header format' |
Settings were rejected before anything was attempted |
'Invalid protocol: ...''No host specified''Invalid host/port: ...''Invalid port: ...' |
The URL could not be parsed |
'Could not initialize Conga ...''neither Conga nor DRC were successfully copied' |
Conga could not be located - see Playing Nicely With Others |
'Conga failed to connect to "..." ...' |
The TCP or TLS connection never came up |
'Unexpected server response: ...' |
The server answered with HTTP rather than upgrading |
'Conga connection timed out' |
Nothing arrived within WaitTime |
'Socket closed by server' |
The server closed the connection instead of answering |
'Unexpected ... at ...' |
A hook called from Connect signalled an error |
The connection-level messages carry Conga's own text, which is usually specific enough to act on:
ws.URL←'wss://no-such-host.invalid' ⋄ ws.Connect
1106 Conga failed to connect to "no-such-host.invalid": ERR_INVALID_HOST Host identification not resolved
ws.URL←'ws://127.0.0.1:9' ⋄ ws.Connect
1111 Conga failed to connect to "127.0.0.1": ERR_CONNECT_DATA Unable to connect to host data port
ws.URL←'wss://expired.badssl.com' ⋄ ws.SSLFlags←0 ⋄ ws.Connect
1202 Conga failed to connect to "expired.badssl.com": ERR_INVALID_PEER_CERTIFICATE Remote certificate is invalid
ERR_INVALID_HOST is a name that did not resolve, ERR_CONNECT_DATA a host that
resolved but refused the connection, and ERR_INVALID_PEER_CERTIFICATE a certificate
that failed the validation asked for by
SSLFlags - see Secure Connections.
Where to Look Next
Which field holds the detail depends on how far the attempt got:
| Symptom | Look at |
|---|---|
| The server answered with HTTP | HttpStatus, HttpMessage, HttpHeaders |
| Conga could not parse the response as HTTP | Data, which holds the unparsed event data |
| The handshake completed but something about it was wrong | WSUpgradeResponse |
| The connection ended up somewhere unexpected | Redirections |
| A proxy is in use | ProxyResponse - see When the Proxy Refuses |
| The connection was made and then died | ErrorInfo and LastWaitResponse |
A worked example of the third row - a server answering 200 OK to an upgrade request
because the path serves ordinary HTTP - looks like this:
ws.URL←'wss://example.com'
ws.Connect
¯1 Unexpected server response: 200 OK
ws.(HttpStatus HttpMessage)
200 OK
Nothing is cleared when a connection ends, so all of these survive for as long as you
need them; Connect clears them only when it is about to make a fresh attempt.
Turning Off the Safety Net
Debug has two useful values while diagnosing:
1disables the error trapping everywhere, so an error insideConnect, inside the listener, or inside one of your hooks suspends where it happened instead of being reduced tomsgorErrorInfo. This is how to develop a hook.2stopsConnectjust before the Conga client is created, which is the moment to inspect the headers, secure parameters, and options that are about to be used.
Debug is a shared field, so setting it affects every instance.