12

I'm new to java and I'm not that familiar with the formatting rules used by an error stack trace when it is thrown and subsequently displayed to an end-user of my web application.

My experience with Oracle database is that the error stack contains internal information, such as schema and procedure names and line number(s), which, while useful for debugging, I would like to prevent the user from seeing. Here's an example:

java.sql.SQLException : ORA-20011: Error description here
ORA-07894: at "NAME_OF_SCHEMA.PROCEDURE_NAME", line 121
ORA-08932: at line 10

The string I want to display to user is Error description here. I can extract this string using regex expressions because I know (1) this string is always on the first line, so I can extract the first line of the error stack trace, and (2) this string always begins with Error and ends with the end of the line. [Note for Oracle users (I don't want to mislead you): the above only applies when using RAISE_APPLICATION_ERROR with an error string starting with Error, otherwise the text Error is not there].

My questions for Java are:

(1) Is there anything potentially sensitive that you wouldn't want users to see in the error stack? If so, what? For example, file paths, server name/IP, etc.

(2) Are there any formatting rules for the Java error stack trace that I can rely on to extract the non-sensitive information? Or, how to others address this concern?

UPDATE 1:

Thanks for all the replies so far, they've been very helpful. While many people comment to use a function such as getUserFriendlyMessage() to map errors to useful user messages, I wonder if someone could expand on this mapping. That is, for the common errors (SQL, I/O, etc.), what "reliable" identifier could be used to search this error stack for to identify the type of error that happened, and then what corresponding text string would you recommend to map to this error message to show to the user? @Adarshr's response below is a good start. For example,

Identified Expected   If found in error stack, display this friendly msg to user
-------------------   ----------------------------------------------------------
SQLException          An error occurred accessing the database. Please contact support at support@companyname.com.
IOException           Connection error(?). Please check your internet connection.

Assume compile-related errors don't need to addressed, but rather focus those errors that end users might experience during normal use. For reference, here's a list of run-time error messages: http://mindprod.com/jgloss/runerrormessages.html#IOEXCEPTION

Alternatively, is it possible to just use the FIRST LINE of the stack trace to display to user? This link is sort of what I was getting at in my original question above:

http://www3.ntu.edu.sg/home/ehchua/programming/howto/ErrorMessages.html

For example, if the identifier Exception is always used, one could simply extract the text that comes between Exception and the end of the first line. I don't know if we can rely on Exception always being there.

ggkmath
  • 4,188
  • 23
  • 72
  • 129

7 Answers7

8

You shouldn't show any of that gobbledygook to your users. It's meaningless to most of them and doesn't help you. As you suspect, it also exposes internals of your implementation that may suggest vulnerabilities that a malicious user might be able to use.

Instead, you should catch exceptions, log them, and show a more comprehensible error message to your users. You can use getMessage() to extract the message part of an exception. If the exception has no message, then show something like "no details available".

UPDATE:

I have some comments based on the question update. First, I would totally insulate the user from any of the internals of your system, both to be kind to the user and for security. (For instance, even knowing that you are using the java.sql package may suggest vulnerabilities to a clever hacker.) So do not use the exception message, the first line of the stack trace, or anything like that when displaying anything to the user.

Second, you should be mapping all errors from the exception level (at which they are experienced in your code) to messages that are at the right level of abstraction for the user. The proper way to do that would depend on the internals of your system and what the user might have been trying to do when the exception was raised. This might mean structuring your system into layers such that each layer that catches an exception translates it into an exception at a higher layer of abstraction. A Java exception can wrap another exceptions (the cause). For instance:

public boolean copyFile(File source, File destination) throws CopyException {
    try {
        // lots of code
        return true;
    } catch (IOException e) {
        throw new CopyException("File copy failed", e);
    }
}

Then this could be used at a higher level in a User class:

public boolean shareFile(File source, User otherUser) throws ShareException {
    if (otherUser.hasBlocked(this) {
        throw new ShareException("You cannot share with that user.");
    }
    try {
        return copyFile(source, otherUser.getSharedFileDestination(source));
    } catch (CopyException e) {
        throw new ShareException("Sharing failed due to an internal error", e);
    }
}

(I hope it's clear that the above code is meant to illustrate the idea of converting exceptions to higher levels of abstraction, not as a suggestion for code that you should use in your system.)

The reason that you want to handle things like this (instead of somehow massaging the message and/or stack trace) is that an exception (for instance an IOException with message "permission denied") may mean totally different things to the user (and to your system) in different contexts.

Ted Hopp
  • 232,168
  • 48
  • 399
  • 521
5

Do not show the exception message / stacktrace directly to the end user. Instead try and use an Exception - Message mapping approach.

For example:

  • SQLException - Sorry, a database error occurred. Please try again later
  • RuntimeException / Exception - Sorry an error occurred. Please try again later

In fact, you can make it as generic as possible. Perhaps you could use a custom error code mapping. For instance, E0001 for SQLException, E0000 for Exception and so on and display this to the end user. This will be helpful when they eventually contact the customer service with the error code.

adarshr
  • 61,315
  • 23
  • 138
  • 167
  • Interesting. Is there a list of keywords I could reliably search for in the stack trace? You mentioned a few above (e.g. SQLException, RuntimeException). It'd be great if you could expand your answer to include a list of reliable keywords that could appear, and appropriate responses. I like what you have so far. – ggkmath Jun 08 '12 at 19:49
  • It really depends on your application. These could even be custom exception classes such as `MyRuntimeException` or `NoSuchProductException`. – adarshr Jun 08 '12 at 21:37
3

You should not show any information to your end users that they cannot understand, which includes all of the stack trace in its entirety. The error you should show when you catch an exception like that should read something like this, in the language of your end-users:

  • Our program has experienced temporary difficulties. Please call technical support line for assistance.

End users couldn't care less about your program's inner organization, databases, stacks, et cetera. All they know is that something that they thought should have work just failed, so they are looking for assistance.

Stack traces are for error logs: you can save them for yourself, or e-mail them to technical support, but you do not want to show them to the end users.

Sergey Kalinichenko
  • 714,442
  • 84
  • 1,110
  • 1,523
  • 2
    I agree in general, but I think in certain situations it can be helpful to provide a little more information. For example, if you get a server timeout, it might just be a problem with their network connection, and if you provide them that information, they might be able to fix it on their own and not have to open a support ticket – Kevin K Jun 08 '12 at 18:43
  • 1
    @KevinK - The only thing most users would need to know is whether the problem is permanent ("it didn't work and it isn't going to work"), temporary ("it didn't work, please try again later"), or correctable by the user ("please log in to leave a comment"). Telling the user that it was "a problem with the network connection" would be meaningless or, worse, intimidating to non-technical users. The program should only display messages that are appropriate to the target user's level of expertise. (Of course, an app for developers can have a more techie feel, but the idea's still the same.) – Ted Hopp Jun 13 '12 at 15:14
1

You should only show stack traces when your app is in debug mode. In production mode, you should show a generic error message (or implement an Exception.getUserFriendlyMessage())and log the error (provide a log id if possible).

Ruan Mendes
  • 90,375
  • 31
  • 153
  • 217
1

The string I want to display to user is Error description here.

You can use Exception.getMessage() for this purpose, though as Juan Mendez suggested, consider implementing a more "user-friendly" error message mechanism for displaying errors to the end user.

If you don't want the end user to see the names of your classes, methods, and fields in the stacktrace, consider using an obfuscator like Proguard. I like to print obfuscated stacktraces in log files, then use ReTrace to unobfuscate them for debugging purposes.

(1) Is there anything potentially sensitive that you wouldn't want users to see in the error stack? If so, what? For example, file paths, server name/IP, etc.

It can depend on the exception being thrown, I think. Choosing to log the exception or not is a tradeoff between application security and being able to effectively diagnose the problem, I take it on a case-by-case basis, and leave comments as necessary to explain my reasoning for logging or not logging it.

Kevin K
  • 9,344
  • 3
  • 37
  • 62
  • I prefer to have a method `getUserFriendlyMessage` so that we're never showing anything too technical – Ruan Mendes Jun 08 '12 at 18:32
  • @JuanMendes agreed, actually I do a similar sort of thing in my code – Kevin K Jun 08 '12 at 18:33
  • @JuanMendes I don't know if I agree with that. If the same exception can be thrown in a variety of different contexts, you might want to display a different error message per context. – Michael Jun 08 '12 at 18:40
  • @Michael, so you want the code handling the exception to display a message based on the type of the exception? I think it makes more sense that the one that created the exception usually knows best what a good error message is. However, sometimes you do want to display a UI specific message, in which case, yes, going by exception type or exception code would be more appropriate. – Ruan Mendes Jun 08 '12 at 18:53
  • @JuanMendes Oh oops sorry I misread. Yeah the string that `getUserFriendlyMessage` returns would be different for each instance of the exception. Like your exception constructor would be `public MyException(String msg, String userFriendlyMsg)` – Michael Jun 08 '12 at 18:55
  • @Michael There are cases when I combine both approaches. The exception user friendly message gives me context from the server side and I add some context on the client. A good example is when trying to save something failed and you want to show the user the reason it failed (document already exists), that has to come from the server, but it can just be an error code in the exception. Then I add some more explanation as what they can do in the UI to fix it. – Ruan Mendes Jun 08 '12 at 19:06
1

Usernames and passwords are obviously sensitive information that you wouldn't want to show to the user. Also, like you said, file system paths, server names, and IP addresses should be hidden as well.

The Exception.getMessage() method will return just the message of the thrown exception and not the entire stack trace. But it looks like in the example you gave, the 2nd and 3rd lines are also part of the exception message, so that doesn't help you.

BUT it's not Good Practice to ever show your users stack traces. Not only can they contain sensitive information, but to the user, they're about as readable as Klingon. You should be logging all uncaught exceptions instead, and then displaying a more user-friendly message to the user.

Michael
  • 34,873
  • 17
  • 75
  • 109
  • Good point. But if there are reliable keywords in what Exception.getMessage() returns, I could search for those and replace this message with something more appropriate. See my UPDATE 1 in the original posting above. – ggkmath Jun 08 '12 at 20:06
1

As others have said, you should not report the stack-trace to users. Several others have suggested you display the getMessage() text. I recommend not doing that. As I've said before:

  • The message was created when the exception was thrown. It therefore at best can provide only very low level information, which can be inappropriate for reporting to a user.
  • Philosophically, using the message seems to me against the whole point of exceptions, which is to separate the detection and initiation of error handling (the throw part) from completion of handling and reporting (the catch part). Using the message means the message must be good for reporting, which moves responsibility for reporting to the location that should be responsible for only detection and initiation. That is, I'd argue that the getMessage() part of the design of Throwable was a mistake.
  • The message is not localised. Despite its name, getLocalizedMessage() is not much good because you might not know what locale you want to use until you catch the exception (is the report to go to a system log read by your English system administrators, or is it to pop up in a window for the French user of the GUI?).
Community
  • 1
  • 1
Raedwald
  • 46,613
  • 43
  • 151
  • 237