aws-sdk-js: AWSError not backed by actual prototype
It seems that the AWSError object that is exported here https://github.com/aws/aws-sdk-js/blob/master/lib/error.d.ts is not actually backed by an actual class. This means that, at runtime, instanceof
checks can have a nondeterministic execution. With transpilation and Chrome, the runtime effect is that the prototype chain of the aws-sdk module object created by webpack transpilation is checked. In Safari, it just throws with one of these instanceof
errors: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Errors/invalid_right_hand_side_instanceof_operand
While I love that we added some typings around AWSError (https://github.com/aws/aws-sdk-js/issues/1219), we should be careful about vending classes if they aren’t backed by actual implementations. The best fix is if the actual prototype backing AWSError can be exported, but I couldn’t find such an implementation. A cursory search for how error objects are created by the SDK surfaces https://github.com/aws/aws-sdk-js/blob/master/lib/util.js#L547, but that doesn’t look like it’s the actually creating the initial AWSError object. The other solution is to vend AWSError as an interface, but this is a potentially breaking change for consumers.
Is there a recommendation here?
About this issue
- Original URL
- State: open
- Created 5 years ago
- Reactions: 40
- Comments: 21 (4 by maintainers)
We are just now running into a problem with this with typescript. When we get an AWSError back from dynamoDB we would like to inspect it to decide what actions to take. Without the ability to use
instanceOf
, we are unable to accomplish this in a type-safe way.I’m not concerned about exceptions that can’t be typed, but the ones that are already typed by the SDK as AWSError. If they are exposed as AWSError and AWSError is a class, then it is idiomatic to use
instanceof
. Checking for a key’s existence as a means of type checking doesn’t provide the same guarantee thatinstanceof
can provide. Any custom error can implement the same key. If I want to have some control flow with regard to my error handling, this will break. If I want to handle an AWSError differently from another error, I’d like some help from the SDK to do so. TS may not type the exceptions but I can surely check the exception’s prototypes and handle accordingly. Ideally AWSError would be a class that I can useinstanceof
on, but if that is not possible it should be vended as an interface so that it is clear that such behavior cannot be depended on.See the following for custom errors and use with typescript:
Maybe my comment isn’t related to what the author states but if you try to use the actual AWSError from the core library as constructor, something like:
You will get
aws_sdk_1.AWSError is not a constructor
on runtime as the actual AWSError class is extended byError
class but doesn’t have any constructor asErrorConstructor
class does.Using aws-sdk v2.602.0 on my package.json file.
Had to make something like code below to get rid of error message:
My use case, similar to @KingDarBoja’s perhaps, was producing my own AWSError instance in a test, in order to unit test how my code responds to an error response from an AWS service.
I was surprised by the lack of a concrete implementation. I think I’d be a lot less surprised if it were an interface.
The solution I mentioned inside the aws-sdk codebase is probably the best we can do until a major version change where the type can be made into an actual class:
To elaborate, I was thinking something like this:
AWSError
instance.isAWSError
function which checks if an object has a key with that private symbol.Could also make use of
Object.defineProperty
to make the symbol non-enumerable (which I think is the default anyway for symbols) so that it doesn’t affect any code which is enumerating the properties of the errors. The result is that code can now very surely verify that something is an AWS error without affecting other code that was using it before.Perhaps a non-breaking way to at least expose the ability to filter out
AWSError
is to add a functionisAWSError
which can act as a type guard. It would be useful outside of TypeScript too.This could probably be easily done by adding a unique symbol key to the errors.
Also ran into this. the fact that this is labelled as
class
means that TypeScript will falsely assume that it’s an actual JavaScript object with a prototype, while in fact there isn’t anything exported in the module namedAWSError
. It should be changed to aninterface
to reflect that.This was the exact same issue I had. We ended up just checking if the error has a
code
property that matched some of the AWS error codes but it’s not ideal.While we’re talking about errors can we also talk about how all of the stack traces are broken? They always only show an internal stack trace of the sdk and lose all of the original stack trace of the calling aplications code.
For example I just saw this:
For some reason the sdk is throwing an unhandled error internally which is only caught by the global handler and it is not being propagated back and not displaying the calling application codes original callstack so i have no idea how to handle it.
Has anyone managed to come up with specific examples as to how to filter out an AWSError in an efficient way?
The type guard would be a great solution now that catches default to unknown https://devblogs.microsoft.com/typescript/announcing-typescript-4-4/#use-unknown-catch-variables
Agreed, but the current plan is to wait until a major version bump to do that. This doesn’t need a major version bump; it’s just a bug fix.