---
# System prepended metadata

title: Error Handling

---

# Error Handling
```
{{["LIVE", "CONFIRMED", "UNCONFIRMED"].includes(currentRow.status) && (table1.data).filter(obj => obj.vehicleID === currentRow.vehicleID && ["LIVE", "CONFIRMED", "UNCONFIRMED"].includes(obj.status)).length > 1 ? 'red' : ""}}

```
**Note**: some Sentry logs have data with nested objects that are shown like `[Object]`. We should probably do better job of displaying the content of the nested objects.

We get logs and errors by error type (from feathers.js)
### NotAuthenticated
- jwt malformed
- invalid login (authentication)

### GeneralError
- A connection already exists for this driver and this date (connection)
- invalid input syntax for type uuid: "null" (user)
- No more available numbers left to allocate (connection/proxyNumber/twilio session)
- sessionUnique must be unique (connection/proxyNumber/twilio session)
- a connection already exists for this driver and this date
- The requested record has been deleted (user/app-hooks)
- Failed to add notification to user (notification/authentication)
- This invoice was not eligible to retry (invoice)
- Connection has already been paid for (handoff/connection-payment)

### NotFound
- No record found for id 'null' (can be any service)

### PaymentError
- This user does not have a stripe customer account (handoff)
- your card has insufficient funds (invoice/stripe charge, connection-payment/)
- Your card does not support this type of purchase (stripe/card)
- Your card was declined (invoice, stripe/charge)

### TypeError
- Cannot destructure property 'phone' of '(intermediate value) (webhook/kustomer)
- converting circular structure to JSON --> starting at object 

### Bad request
- invalid connection status. Connection must be UNCONFIRMED, CONFIRMED
- notNull Violation: notification.userID cannot be null
- You passed an empty string for 'statement_descriptor_suffix' (stripe/charge)

### Forbidden
- Error (vehicle/hooks)

### HttpError
- Verification code is incorrect
- 403 Phone number not provisioned with any carrier

### ValidationFailedError
- Validation failed (authentication)

We can create 3 types of errors by extending from the FeathersError class and calling its constructor with (msg, name, code, className, data). Like this:

```
const { FeathersError } = require('@feathersjs/errors');

class UnsupportedMediaType extends FeathersError {
  constructor(message, data) {
    super(message, 'unsupported-media-type', 415, 'UnsupportedMediaType', data);
  }
}

const error = new UnsupportedMediaType('Not supported');
```

### 3 Error types:
- Non-critical/known
  - operational expected user
  - operational expected third-party errors
- Critical known
  - operational infrastructure
  - operational third-party
- Critical unknown
  - developer errors



**Possible solution**: Returning errors from third party services so we can check against them and wrap them in "Non-critical/known" format. So if we call `stripe` service in `handoff`, and there is an error from stripe:

```typescript
// inside handoff service
const _, stripeError = await chargeService.patch(charge.id, {
    status: newStripeCharge.status,
    captured: newStripeCharge.captured,
    charge: newStripeCharge,
});

if (stripeError) {
  // 
}
```
Because we explicitly saved the error from Stripe, we know that it's not our code, so we could throw it as non-critical. Any other error in handoff service would probably mean some issue with our code, so qwe could throw it as "Critical unknown".

Whatever errors out in our native service we can wrap in  "Critical known".
We have this error context in app hooks which is we are setting before sending to Sentry. I think it's really helpful and can be kept for future use.

```typescript
const errorContext = {
    userType: context.params?.user ? context.params?.user?.type : 'none',
    userPhone: context.params?.user
      ? context.params?.user?.phoneNumber
      : 'none',
    method: context.method,
    service: context.path,
    data: context.data ? context.data : 'none',
    query: context.params?.query,
    id: context.id ? context.id : 'none',
};
```

Although, this is not always full. One option is to get the error object from third-party service and add additional data from that service that made request before throwing the error.

When should we throw an error and when not?

Expected
Operational
Developer

stop GeneralError

###### tags: oxo