Javascript doesn't catch error in WebSocket instantiation
Asked Answered
B

3

66

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.

Blah answered 23/6, 2015 at 12:17 Comment(1)
Great question. Same issue here.Ligniform
S
119

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.

Statue answered 23/6, 2015 at 12:38 Comment(16)
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.Blah
@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.Statue
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?Musa
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.Rondarondeau
is it possible to catch these error with async and await or with promises?Aglimmer
@Aglimmer 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. 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.Statue
Yes, I discovered the same thing by my self @apsillers, thank you for answering the question.Aglimmer
This is not good. My web client polls not running server each 1 second and writes each time to the console log this error.Divulsion
looks like @Statue answer no longer works as I see an error message using Chrome 74Dasteel
@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.Statue
@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.Statue
@Statue 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.Kirimia
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.Rudiger
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. @StatueMcfarland
We need a Promise version of WebSocket.Haslett
@RobHawkins It may be a bit late, but I have fixed this issue in my fork of the webpack plugin you mentioned. By requesting a HTTP server for reconnecting. Please take a look at github.com/reorx/webpack-ext-reloader/releases/tag/1.6.0Xanthochroism
E
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/

Exosmosis answered 23/6, 2015 at 12:22 Comment(2)
That's due to a different error, though: "An insecure WebSocket connection may not be initiated from a page loaded over HTTPS."Orientate
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.Blah
H
0

You can get the error in the onclose method;

var websockets =  new WebSocket('ws://127.0.0.1:9999/websocket');
var isOpen = false;
websockets.onopen = (event) => {
  isOpen = true;
};
websockets.onclose = (event) => {
  console.log(event)
  if (!isOpen) {
    // get the error
  }
};
Hairbrush answered 7/3 at 9:22 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.