> ## Documentation Index
> Fetch the complete documentation index at: https://docs.conductor.is/llms.txt
> Use this file to discover all available pages before exploring further.

# Error handling

> Catch and respond to connection errors, invalid requests, data problems, and more.

One of the most complicated aspects of QuickBooks Desktop is its error handling. QuickBooks errors are often cryptic and unhelpful, and they can arise from several sources (e.g., Web Connector, QB request processor, QuickBooks Desktop itself), each using a different format and mechanism. Sometimes, their errors do not even describe what went wrong when they could! **Conductor unifies and simplifies these errors into a single, consistent error format and augments each with our own user-friendly language that describes how to resolve the issue.**

With Conductor, every [error](/api-ref/errors) includes plenty of useful information.

## Error types

Any error object you receive will be an instance of one of the following error [types](/api-ref/errors#schema-type):

| Error Type | Description |
| - | - |
| `INTEGRATION_ERROR` | Raised when the third-party integration encounters an error while processing the end-user's request. This often results from an issue with the request or data handling that requires your attention to resolve.<br /><br />E.g., a `ListID` you provided was not found in QuickBooks Desktop, or an accounting value you supplied did not adhere to the integration's accounting rules.<br /><br />Refer to `error.integrationCode` for the error code returned by the integration, if available. |
| `INTEGRATION_CONNECTION_ERROR` | Raised when a connection error occurs with the third-party integration on the end-user's side. This typically indicates an issue with the end-user's connection or configuration, which they must resolve. In other words, you cannot take action to fix these errors.<br /><br />E.g., the Web Connector failed to connect to QuickBooks Desktop on the end-user's computer.<br /><br />Refer to `error.integrationCode` for the error code returned by the integration connector, if available.<br /><br />❗ We recommend *not* triggering alerts for these errors because only the end-user can fix them. See [Global error handling](#global-error-handling) for an example of this. |
| `INVALID_REQUEST_ERROR` | Raised when you make an API call with the wrong parameters, in the wrong state, or in an invalid way. |
| `AUTHENTICATION_ERROR` | Raised when Conductor cannot authenticate you with the credentials you provided. E.g., an incorrect API key. |
| `ConductorPermissionError` | Raised when you attempt to access a resource that is not allowed. |
| `ConductorConnectionError` | Raised when there was a network problem between the client (on your server) and Conductor's servers. E.g., a downed network or a bad TLS certificate. |
| `ConductorInternalError` | Raised when something went wrong on Conductor's end. (These are rare.) |

## Common error codes

| Error Code | Description |
| - | - |
| `INTEGRATION_CONNECTION_NOT_SET_UP` | Returned when the end-user has never opened the auth flow, or when Conductor has not received a heartbeat from their Web Connector since the connection was created or since its most recent configuration file download. This normally means the end-user has not finished the auth flow. |
| `INTEGRATION_CONNECTION_NOT_ACTIVE` | Indicates that the Web Connector connection is offline. This error occurs when Conductor hasn't received a heartbeat from Web Connector within the request's [timeout period](/api-ref/timeouts), suggesting the connector isn't running or is experiencing issues on the end-user's machine. |
| `QBD_CONNECTION_ERROR` | Raised when the Web Connector is online but cannot establish a connection with QuickBooks Desktop. Common causes include having the wrong company file open, QuickBooks Desktop not running, or insufficient permissions. |
| `QBD_COMPANY_FILE_MISMATCH` | QuickBooks Desktop has the same company open at a different path than this connection saved, or a copy of it (for example, a restored backup with a different file name). The Web Connector is online and QuickBooks is open. The error message names both paths. Resolve it by having the end-user open the expected file, or, if the open file is the one they want to keep using, by resetting the saved company file path from the dashboard or the setup flow while that file is open. The error repeats on every request until then, so do not retry automatically. |
| `QBD_WRONG_COMPANY_FILE_OPEN` | QuickBooks Desktop has a different company or a sample company open instead of the one this connection expects. The end-user must open the correct company file in QuickBooks Desktop. If they intentionally switched this connection to a different company file, reset the saved company file path while that file is open. |
| `QBD_REQUEST_ERROR` | Indicates a QuickBooks Desktop business logic error. These errors occur when a request violates QuickBooks' business rules - for example, attempting to create a bank account with a negative opening balance or using an invalid reference to another entity. |
| `QBD_REQUEST_TIMEOUT` | Occurs when QuickBooks Desktop successfully connects but fails to respond within the expected timeframe. This can happen in scenarios where QuickBooks needs to be launched and load a large company file, or when processing requests involving substantial amounts of data. |

<Note>
  These error codes are accessible via the `error.code` property of any
  Conductor error. For integration-specific errors, you can also reference
  `error.integrationCode` for the raw error code from QuickBooks Desktop.

  For `QBD_REQUEST_ERROR`, `error.integrationCode` is the qbXML status code
  that QuickBooks Desktop returned (e.g., `"3200"`), passed through unchanged.
  Intuit documents these codes in its [status code
  reference](https://developer.intuit.com/app/developer/qbdesktop/docs/develop/exploring-the-quickbooks-desktop-sdk/status-codes-in-response-messages).
  Conductor automatically retries only two of them: `3390` and the transient
  form of `3180` ("modified by another user"); every other QuickBooks Desktop
  error is returned to you as-is.
</Note>

## Error messages

Every Conductor [error](/api-ref/errors) includes two message fields:

1. `error.message`: The primary error message. Log it for debugging, and use it in developer/admin surfaces or test projects.
2. `error.userFacingMessage`: The user-friendly message we recommend showing to end-users. It may match `error.message`, or it may mask details that are only useful to developers or administrators, such as invalid API keys, billing issues, or setup problems.

<Note>
  If your app already displays `error.userFacingMessage`, no change is required.
  We still recommend it for end-user UI. Use `error.message` for logs,
  developer/admin surfaces, test projects, and other places where the primary
  error detail is useful.
</Note>

For example, this invalid API key error keeps developer-only detail in `message` while masking `userFacingMessage`:

```json Example Conductor error object wrap theme={"system"}
{
  "error": {
    // Primary message for logs and developer/admin surfaces.
    "message": "Invalid API key provided: 'sk_live_...1234'",
    // User-friendly message for end-user UI.
    "userFacingMessage": "An internal server error occurred. Please try again.",
    "type": "AUTHENTICATION_ERROR",
    "code": "API_KEY_INVALID",
    "httpStatusCode": 401,
    "requestId": "req_1234567890"
  }
}
```

## Error handling in our SDKs

Our errors in our SDKs are a bit janky at the moment. We're working on it. For now, we recommend the approach shown below for typing and unwrapping Conductor errors from our SDK. The example below is for our Node.js SDK, but the approach would be similar for our Python SDK.

```ts Check the connection status theme={"system"}
import Conductor, { APIError as ConductorError } from "conductor-node";

// Yes, manually define this inline.
interface IConductorError {
  message: string;
  userFacingMessage: string;
  type: string;
  code: string;
  httpStatusCode: number;
  integrationCode?: string;
  requestId: string;
}

const conductor = new Conductor({ apiKey: "{{YOUR_SECRET_KEY}}" });

try {
  // Check the connection status
  await conductor.qbd.healthCheck({ conductorEndUserId: "{{END_USER_ID}}" });
  updateUI("QuickBooks Desktop is successfully connected!");
} catch (error) {
  if (error instanceof ConductorError) {
    // Unwrap Conductor error
    const conductorError = error?.error?.error as IConductorError;

    if (conductorError.code === "INTEGRATION_CONNECTION_NOT_SET_UP") {
      // This specific error code indicates the user needs to go through the auth
      // flow. Either they never started it, or didn't complete it.
      const authSession = await conductor.authSessions.create({
        publishableKey: "{{YOUR_PUBLISHABLE_KEY}}",
        endUserId: "{{END_USER_ID}}",
      });
      updateUI(
        "Set up your QuickBooks Desktop connection: " + authSession.authFlowUrl,
      );
    } else {
      // Prefer the user-friendly message for end-user UI.
      updateUI("An error occurred: " + conductorError.userFacingMessage);
    }
  } else {
    throw error;
  }
}
```

## Specific error handling

If you need special handling for specific errors, you can wrap individual API calls, as shown below.

Using `async`/`await`:

```ts theme={"system"}
import Conductor, { APIError as ConductorError } from "conductor-node";

const conductor = new Conductor({ apiKey: "{{YOUR_SECRET_KEY}}" });

try {
  const newAccount = await conductor.qbd.accounts.create({
    conductorEndUserId: "end_usr_1234567abcdefg",
    accountType: "bank",
    name: "Accounts-Payable",
  });
} catch (error) {
  if (error instanceof ConductorError) {
    // Unwrap Conductor error
    const conductorError = error?.error?.error;
    // Check `conductorError.code`, `conductorError.integrationCode`, etc., for special handling.
  } else {
    // ...
  }
}
```

Or in the form of a rejected promise:

```ts theme={"system"}
import Conductor, { APIError as ConductorError } from "conductor-node";

const conductor = new Conductor({ apiKey: "{{YOUR_SECRET_KEY}}" });

conductor.qbd.accounts
  .create({
    conductorEndUserId: "end_usr_1234567abcdefg",
    accountType: "bank",
    name: "Accounts-Payable",
  })
  .then((newAccount) => {
    // ...
  })
  .catch((error) => {
    if (error instanceof ConductorError) {
      // Check `error.code`, `error.integrationCode`, etc., for special handling.
    } else {
      // ...
    }
  });
```

## Global error handling

It is unnecessary to wrap each API call individually, as demonstrated in the examples above. Instead, we suggest implementing a global error handler for your server, such as [`app.use((error, ...) => { ... })` in Express](https://expressjs.com/en/guide/error-handling.html#writing-error-handlers). Within this handler, perform the following actions:

1. For any `ConductorError` instance, display the `error.userFacingMessage` property to the end-user in your app's UI while logging the complete error object. The primary `error.message` is also useful for logs, developer/admin surfaces, and test projects.
2. For all `ConductorError` instances, transmit the full error object to your error-tracking service (e.g., Sentry):
   * Send a **warning** for instances of `INTEGRATION_CONNECTION_ERROR`, which are not actionable by you and can only be resolved by the end-user; for example, failure to connect to QuickBooks Desktop on the end-user's computer.
   * Send an **error** for all other `ConductorError` instances, such as an invalid API key.

For example, using an [Express error handler](https://expressjs.com/en/guide/error-handling.html#writing-error-handlers):

```ts theme={"system"}
import * as Sentry from "@sentry/node";
import Conductor, { APIError as ConductorError } from "conductor-node";

const conductor = new Conductor({ apiKey: "{{YOUR_SECRET_KEY}}" });

app.use((error, req, res, next) => {
  if (error instanceof ConductorError) {
    // Unwrap Conductor error
    const conductorError = error?.error?.error as IConductorError;

    Sentry.captureException(error, {
      level:
        conductorError.type === "INTEGRATION_CONNECTION_ERROR"
          ? "warning"
          : "error",
    });
    // Return the user-friendly message for your end-user UI.
    res
      .status(500)
      .send({ error: { message: conductorError.userFacingMessage } });
  } else {
    // ...
  }
});
```


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.