118

Since we can throw anything with the throw keyword in Javascript, can't we just throw an error message string directly?

Does anyone know any catch in this?

Let me add some background to this: Very often, in the JavaScript world, people rely on parameter checking as opposed to using the try-catch mechanism, so it makes sense to only throw fatal errors with throw. Still, to be able to catch some system Errors, I have to use a different class for my own errors and instead of creating a subclass of Error, I think I should just use String.

Mark Amery
  • 143,130
  • 81
  • 406
  • 459
billc.cn
  • 7,187
  • 3
  • 39
  • 79
  • 3
    While it's possible, does it make sense? I'd rather catch an error than a string. – Rob W Jul 16 '12 at 09:59
  • 1
    related: [What is the difference between `throw new Error` and `throw someObject` in JavaScript?](http://stackoverflow.com/q/9156176/1048572) – Bergi Jun 09 '16 at 17:27
  • exports.login = (req, res) => { login(req).then(({ token, user }) => { res.send(token); }).catch(err => { if(typeof err === 'string') { res.status(401).send(err); } else { console.log(err); res.status(500).send('Something went wrong'); } }); } I do handle errors this way, I am also confused, am I doing it right way. – Md Adil Oct 27 '17 at 06:56

6 Answers6

91

While it is okay possible to throw any value, it is generally considered poor form to throw anything other than an instance of Error or one of its subclasses. There are several reasons for this:

  1. Catching code may expect the thrown object to have the usual message, stacktrace, and name properties that appear on Errors.
  2. Lack of a stacktrace makes debugging problematic, especially in the case of uncaught exceptions / unhandled rejections. E.g. Debugging an "Uncaught [Object object]" error can be particularly painful.
broofa
  • 37,461
  • 11
  • 73
  • 73
lanzz
  • 42,060
  • 10
  • 89
  • 98
  • 12
    For example, it may assume that the `.message` property of the [`Error`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error) is a string, try to call a string method on it, and explode. – Mark Amery Feb 14 '15 at 19:08
  • 8
    You *can*, but I wouldn't say it's *"okay"*. – 1j01 Oct 22 '16 at 02:10
  • 9
    This is an awful idea since it means you will have a much harder time debugging the bug without a stack trace. – Benjamin Gruenbaum Oct 19 '17 at 13:37
  • 4
    @BenjaminGruenbaum If you throw a string in Chrome, you do get a stack trace. E.g ` function a() { throw "Hello World"; } a();` will have a stacktrace like this: `a @ main.js:4 (anonymous) @ main.js:7` – doubleOrt Oct 26 '17 at 18:05
  • 1
    @Taurus thanks, I didn't know that - now try doing it with the debugger not connected on in firefox :) – Benjamin Gruenbaum Oct 26 '17 at 18:40
  • Yes, that's true as well. – doubleOrt Oct 26 '17 at 18:44
  • Sometimes, as in my particular case, you don't want an obvious stack trace advertised to the user in the fatal error :) – redfox05 Feb 23 '18 at 22:18
  • @doubleOrt While a stacktrace is created for thrown strings, it is not preserved if the string is caught and then re-thrown (confirmed just now in testing). So in a context where catch+rethrowing can occur, throwing strings instead of errors can lose debugging information. – Venryx Oct 16 '21 at 09:46
  • Although I support including stacktrace, if you can't tell the error right away from the error message, you most likely wrote an unclear error message. – jave.web Apr 10 '22 at 15:54
  • The error message tells you _what_ happened, the stack trace helps you figure out _why_ – lanzz Apr 10 '22 at 19:37
66

Yes, you can throw other values, but it's not a good practice.

Does anyone know any catch in this?

A string is not an error object, and does not convey any useful debugging information. Devtools rely on that, such as the file and line where the error was created, the stacktrace at the throw location etc, which are available as properties on Error objects.

Whenever you think of throwing a primitive string value, throw a new Error("<the string>") instead.

Vince Bowdren
  • 8,326
  • 3
  • 31
  • 56
Bergi
  • 630,263
  • 148
  • 957
  • 1,375
  • 13
    It's worth noting that the Chrome devtools will print a stack trace even if you throw a string. You lose the `.stack` property which can be useful for some automated error handling (e.g. when setting up automated reporting of errors to a server to let you debug things that go wrong in production), but that's more of an edge case; day-to-day manual debugging in-browser works fine if you throw strings. – Mark Amery Feb 14 '15 at 19:04
  • 2
    Also note that there is a speed advantage to throwing (and catching) strings instead of errors: https://jsbench.me/1gkqk6dm06/2 My results: 237k ops/s for errors, 1.168m ops/s for strings. This almost never matters, but theoretically could. (eg. if you use string-throwing simply to "escape" to a higher-level function-call for some reason) – Venryx Jul 01 '21 at 00:38
  • @Venryx If you want to use exceptions for control flow, not for errors, I'd recommend not use strings either (although of course I would recommend not to do this at all). `throw null;`, if you must, or if you need different return codes, use symbols or constant objects. – Bergi Jul 01 '21 at 01:39
  • @Bergi Thank you for your comment. However, I extended the perf-test just now, and `throw null;` appears to only be ~1% faster than `throw "some string";`: https://jsbench.me/1gkqk6dm06/3 Perhaps the latest versions of V8 have optimized them to where they're about equivalent? – Venryx Jul 01 '21 at 05:35
  • 1
    @Venryx I didn't mean to say that throwing `null` would be faster, but only that it was cleaner. You just shouldn't `throw "Test1"` because it's confusing. – Bergi Jul 01 '21 at 08:38
  • If you're worrying about exception speed you're doing it wrong. Exceptions are exactly what they say, they're an exception, something that happens rarely. They're not meant to be optimized, they're meant to do something out of the ordinary. If you're relying on exceptions to be fast you should arguably refactor your code to not be using an exception for whatever purpose you're using it for. If you're expecting it to happen a lot then by definition, it's not an exception. – gman Oct 24 '22 at 06:01
  • @Venryx didn't know you could throw a string! for the cases where I throw my own errors that's usually all I need – 1.21 gigawatts May 16 '23 at 04:49
26

You can throw errors with messages, you know.

try {
    throw new Error("This is an error");
} catch (e) {
    alert(e.message); // This is an error
}

But you can actually throw strings:

try {
    throw "This is an error";
} catch (e) {
    alert(e); // This is an error
}
MaxArt
  • 22,200
  • 10
  • 82
  • 81
  • 5
    Sorry for not making the questions clear. I know these all work, but I am interested in the potential problems with using Strings directly as opposed to using an Error object. – billc.cn Jul 16 '12 at 12:21
  • Ah, ok, then just have a look to Ianzz's comment. It sums up all the caveats in that. But my advice is still to throw `Error` objects with custom messages. It saves you the trouble to check for the error type to tell 'your' errors from other natively-generated errors... Unless, of course, you want to do that on purpose (then maybe re-throwing the `Error` objects in the `catch` scope). – MaxArt Jul 16 '12 at 15:41
5

As others have mentioned above, if you are not throwing an Error object, then you must have try/catch blocks to trap these objects and handle them appropriately or else be in a world of hurt for debugging.

However, when it comes to throwing Errors for non-error handling purposes like controlling program flow, this may be a helpful way to utilize throw without an Error.

When using throws to control program flow, it can be inefficient in any language as the runtime will often do a lot of heavy lifting to unwind call stack information and serialize the data so its available to the user land scope. By avoiding Error creation, you can avoid this performance hit. The key is that you must have a handler up the call stack that knows how to handle this situation. For instance if you throw {isHardStop: true, stopCode: SOME_CODE} and design the handlers to detect this, you may be able to flatten out some of your code or choose cleaner syntax.

Your handler for this ladder case could be structured like:

try { ... } catch(thr) {
    if(!thr){
       // Is not Error or Json - Handle accordingly
    } else if(thr.isHardStop){
       // Handle the stop
    } else {
       // Most likely a real error. Handle accordingly
    }
}
Timothy C. Quinn
  • 3,739
  • 1
  • 35
  • 47
  • No, If you need to drive program flow you return/resolve with a value that can handle program flow. An exception is exceptional. If you can not recover and need the system to throw an exception then this is not for program flow but for exception handling. – Sukima Nov 15 '18 at 16:15
  • I agree that errors and exceptions should not be thrown for purposes other than exception handling. However, in my example I am just throwing an object and not instantiating an error and thus its just using the try / catch mechanism for an alternative flow control. There should be a negligible performance hit but trainability is not ideal. With that said, have never done this myself and probably wouldn't these days. Instead, I would probably use a method like used in Erlang / Elixir by wrapping the logic in a lambda that returns a success identifier and a result value. – Timothy C. Quinn Nov 16 '18 at 17:37
3

Although you can throw any type of data that you’d like, this is not optimal when debugging. A JS Error object contains all kind of information regarding the Error and a message. Whereas a string can only contain a message.

This additional information includes:

  1. fileName: from which file the error was thrown
  2. Linenumber: from which line the error was thrown
  3. stacktrace: from which function the error was called

Here is for example a stacktrace from chrome devtools:

enter image description here

Willem van der Veen
  • 33,665
  • 16
  • 190
  • 155
0

Catch for error message strings like the following:-

try {
    throw new Error("This is an error");
} catch (e) {
    const errorString1 = String(e) // errorString1 = "Error: This is an error"
    const errorString2 = e.message // errorString2 = "This is an error"
}
ashuvssut
  • 1,725
  • 9
  • 17