65

My socket currently throws net::ERR_CONNECTION_REFUSED because the server isn't running, which I want it to do at the moment.

The problem is that the following piece of code doesn't catch the error. In the console, I see an exception on line 2 (with net::ERR_CONNECTION_REFUSED) which I believe shouldn't happen as it's within a try statement.

try {
  ws = new WebSocket('ws://'+ host + ':' + port + '/');
}
catch (err) {
  console.log('This never prints');
}
ws.onerror = function (error) {
  console.log(error);
};

So my question is why is it not being caught?

What I ultimately want is for the error message to be displayed elsewhere, but I can't catch it, and line 8 prints an "event" object which doesn't mention net::ERR_CONNECTION_REFUSED, so I'm not sure how to get the error message out.

Syed M. Sannan
  • 1,061
  • 2
  • 9
  • 28
andyhasit
  • 14,137
  • 7
  • 49
  • 51

2 Answers2

115

The WebSocket's connection-time error causes a dispatched event, not a thrown value. This is because throw operations must be synchronous. In order to handle all connection-time errors as thrown errors, the WebSocket constructor would need to completely suspend all script execution and UI interaction until the entire WebSocket handshake had completed. Instead, the connection process runs asynchronously, thereby allowing the browser thread to continue working while the WebSocket connection initializes in the background. Because of the connection's asynchronous nature, the WebSocket must report errors via error events, since the synchronous new WebSocket operation has already finished by the time the asynchronous connection task encounters an error.

The ERR_CONNECTION_REFUSED message you see is purely for the benefit of developers; it is not accessible to the script in any way. It does not have any representation within the JavaScript environment. It's just a red-colored message that appears in your console to inform you, the human looking at the browser, about an error.

The error handler event is the correct place to respond to failure, but the lack of script-readable connection-time error information is by design. From the WHATWG spec for the WebSocket API:

User agents must not convey any failure information to scripts in a way that would allow a script to distinguish the following situations:

  • A server whose host name could not be resolved.
  • A server to which packets could not successfully be routed.
  • A server that refused the connection on the specified port.
  • A server that failed to correctly perform a TLS handshake (e.g., the server certificate can't be verified).
  • A server that did not complete the opening handshake (e.g. because it was not a WebSocket server).
  • A WebSocket server that sent a correct opening handshake, but that specified options that caused the client to drop the connection (e.g. the server specified a subprotocol that the client did not offer).
  • A WebSocket server that abruptly closed the connection after successfully completing the opening handshake.

[...] Allowing a script to distinguish these cases would allow a script to probe the user's local network in preparation for an attack.

The browser is deliberately omitting any useful information as required by the spec. The spec authors are concerned that access to this information could allow a malicious Web page to gain information about your network, so they require browsers report all connection-time errors in an indistinguishable way.

apsillers
  • 112,806
  • 17
  • 235
  • 239
  • 6
    That is very helpful information, though I don't see why the exception is not "caught". That seems to break the fundamental framework of the language. I presume that the way to do what I want would be to handle onclose and look at the CloseEvent's code. – andyhasit Jun 23 '15 at 12:48
  • 5
    @AndyHasIt I added an opening paragraph about that; I hope it's clear enough. Basically, we can't `catch` an error because we synchronously exit the `try` block before the the asynchronous connection attempt encounters an error. – apsillers Jun 23 '15 at 12:58
  • 2
    Thank you very much. Now I know where that error comes from. But what if I have made an onerror handler and now just want to remove the dispatched event error in the console because I like to keep the console clean for end users? – Sámal Rasmussen Feb 08 '17 at 13:48
  • 7
    How can I prevent the error from showing up in console though? none of `ws.onerror = ev => { ev.stopImmediatePropagation() ev.stopPropagation() ev.preventDefault() return false }` helps at all. – Mihail Malostanidis Mar 26 '18 at 00:37
  • 1
    is it possible to catch these error with async and await or with promises? – OuuGiii Nov 27 '18 at 13:35
  • 4
    @OuuGiii You *could* do with with a Promise `catch` approach, but the `WebSocket` API is not Promise-aware, so you need to make your own Promise objects. See [this fiddle I just made](http://jsfiddle.net/gyxjq0ma/). You still have to listen for the error-event and call `reject` to make any `catch` mechanics trigger. You can't even `throw` because the `onerror` resolution is happening chronologically outside the `new Promise` function. Again, all a WebSocket constructor does on connection-failure is dispatch an event that happens to be named `error`; it doesn't raise an exception. – apsillers Nov 27 '18 at 13:50
  • Yes, I discovered the same thing by my self @apsillers, thank you for answering the question. – OuuGiii Nov 27 '18 at 15:02
  • 3
    This is not good. My web client polls not running server each 1 second and writes each time to the console log this error. – Viacheslav Dobromyslov Jan 01 '19 at 15:10
  • looks like @apsillers answer no longer works as I see an error message using Chrome 74 – Simon H May 12 '19 at 10:15
  • @SimonH Could you clarify what you mean? My answer agrees with the OP's question that you *should* see an error message in the console (but *not* be able to programmatically access that error message). The WebSocket will also dispatch an `error` event. This question asks about handling that error in a `catch` block, which is not possible. – apsillers May 12 '19 at 11:57
  • @Casey It's been a while since ai looked at this, but my guess is that cross-origin requests *don't* expose 401 errors, do they? Only same-origin requests would. For better or worse, Websockets are designed to be origin-agnostic, which means they withhold error details as strictly as for a cross-origin request. – apsillers Aug 12 '19 at 18:08
  • @apsillers I am not sure about the cross-origin get requests exposing errors. I am just frustrated that nothing in browser land has any level of consistency (such as same-site requests exposing error details for both http and websockets). Seems like the same type of logic that decided selects (ie. dropdowns) are rendered by the native OS (therefore making it impossible to style with css), or that checkboxes should use the "checked" property instead of the "value" property, or having the "readonly" property on a checkbox input have to meaning. – Casey Aug 12 '19 at 19:04
  • During browser extension development, using a time-saving tool called `webpack-extension-reloader`, this error appears in the extension's box on `chrome://extensions`. That is the same location errors for background scripts appear. If you don't use that tool, you need to manually click reload on the extension page and all impacted webpages for every code change. The tool saves time by reconnecting after pauses in development, and without that, you need to remember to click reload. So, due to this error, you must either clear the error or reload code every time you develop. – Rob Hawkins Sep 26 '19 at 06:01
  • What does it mean when I replace the fiddle with my own url (I allow all origins in backend) and see `"Catch handler sees: ", "couldn't connect"`? It does not make any sense. @apsillers – Akcore Jan 08 '22 at 02:19
  • We need a Promise version of WebSocket. – Jonathan Nov 25 '22 at 08:38
0

I tried creating fiddle with it and I am able to see line printed.

host='localhost';
port=100;
try {

    ws = new WebSocket('ws://'+ host + ':' + port + '/');
  }
  catch (err) {
    console.log('This never prints');
  }
  ws.onerror = function (error) {
    console.log(error);
  };

https://jsfiddle.net/xzumgag0/

Kunal Pradhan
  • 522
  • 1
  • 5
  • 12
  • 4
    That's due to a different error, though: "An insecure WebSocket connection may not be initiated from a page loaded over HTTPS." – dfreeman Jun 23 '15 at 12:29
  • Indeed, the jsfiddle example catches the error, but if you open your console and run the same it doesn't catch the error. As dfreeman points out these are different errors. – andyhasit Jun 23 '15 at 12:34