Error Handling
How the nXus SDK surfaces errors, what each field means, and how to handle authentication failures, validation errors, rate limits, and QuickBooks connection issues.
Error shape
Every error response from the nXus API returns the same StandardErrorResponse shape — a single, consistent structure across all status codes.
{
"error": {
"message": "The provided API key is invalid or has been revoked.",
"userFacingMessage": "Your API key is no longer valid. Create or rotate a key and try again.",
"type": "AUTHENTICATION_ERROR_TYPE",
"code": "API_KEY_INVALID",
"httpStatusCode": 401,
"integrationCode": null,
"requestId": "req_abc123"
}
}| Field | Purpose |
|---|---|
message | Developer-facing detail. May include QuickBooks-specific codes or internal context. |
userFacingMessage | Safe to display to end users — written for non-technical operators. |
type | Broad error category (e.g. AUTHENTICATION_ERROR_TYPE, RATE_LIMIT_ERROR_TYPE). |
code | Specific error code for programmatic handling (e.g. API_KEY_INVALID, QBD_CONNECTION_ERROR). |
httpStatusCode | The HTTP status code. |
integrationCode | QuickBooks-specific detail when available — an HRESULT hex code or QBXML status code. null for non-QB errors. |
requestId | Unique request identifier (req_...). Include this in support tickets. |
For the full list of type and code values, see the Error Responses API reference.
Using NxusApiError
Both the TypeScript and Python SDKs include a typed NxusApiError class that wraps the raw error response and provides helper properties for common checks.
import { NxusApiError } from '@nxus/sdk';Converting SDK errors
Every SDK method returns an error value when the request fails. Use NxusApiError to get a typed error with all fields parsed:
const { data, error } = await nxus.listVendors();
if (error) {
const err = NxusApiError.from(error);
console.log(err.userMessage); // Always safe for UI
console.log(err.code); // __CODE_TOKEN_0__, __CODE_TOKEN_1__, etc.
console.log(err.status); // 401, 502, etc.
console.log(err.requestId); // __CODE_TOKEN_2__
console.log(err.integrationCode); // __CODE_TOKEN_3__ or null
}Or use the throwIfError helper to throw typed errors automatically:
import { throwIfError } from '@nxus/sdk';
try {
const { data, error } = await nxus.getVendor({ path: { id: vendorId } });
throwIfError(error);
// data is narrowed to the success type here
return data;
} catch (err) {
if (err instanceof NxusApiError) {
console.error(`[${err.requestId}] ${err.code}: ${err.userMessage}`);
}
}Boolean helpers
Instead of switch statements and status code checks, use the built-in helpers:
const { data, error } = await nxus.createVendor({ body: vendor });
if (error) {
const err = NxusApiError.from(error);
if (err.isAuthError) {
// 401 or 403 — prompt for new credentials
promptForNewKey();
} else if (err.isValidationError) {
// 422 — map field errors to your form
highlightFields(err.validationErrors);
} else if (err.isRateLimited) {
// 429 — wait for retryAfter, then try again
showRetryAfter();
} else if (err.isConflict) {
// 409 — stale edit sequence, re-fetch and retry
showRefreshPrompt();
} else if (err.isNotFound) {
// 404 — resource doesn't exist
showNotFound();
} else if (err.isIntegrationError) {
// 502/503 — QuickBooks Desktop is unreachable
showConnectionStatus(err.integrationCode);
}
// Always safe to show to users
showToast(err.userMessage);
// Always log for debugging
console.error(`[${err.requestId}] ${err.type}/${err.code}: ${err.message}`);
}| TypeScript | Python | Matches |
|---|---|---|
isAuthError | is_auth_error | 401, 403, or AUTHENTICATION_ERROR_TYPE |
isValidationError | is_validation_error | 422 with per-field validation errors |
isRateLimited | is_rate_limited | 429 or RATE_LIMIT_EXCEEDED code |
isConflict | is_conflict | 409 or QBD_STALE_EDIT_SEQUENCE code |
isNotFound | is_not_found | 404 or NOT_FOUND_ERROR_TYPE |
isIntegrationError | is_integration_error | Has a QuickBooks integrationCode |
Status code reference
| Status | Meaning | What to do |
|---|---|---|
| 400 | Bad request — malformed JSON or invalid argument | Fix the request payload. |
| 401 | Unauthorized — missing, invalid, expired, or revoked API key | Check the Authorization header and rotate the key if needed. |
| 402 | Payment required — no active subscription for production mode | Complete billing setup, then retry the request. |
| 403 | Forbidden — valid key but insufficient permissions | Verify the key has access to the requested resource. |
| 404 | Not found — the QuickBooks object doesn’t exist | Verify the ID or list ID. |
| 405 | Method not allowed | Check the HTTP method against the endpoint docs. |
| 408 | Request timeout — the operation exceeded the time limit | Verify QuickBooks Desktop is responsive. The COM call already failed. |
| 409 | Conflict — stale edit sequence | Re-fetch the record, get the current editSequence, and retry the update. |
| 422 | Validation failed — one or more fields are invalid | Fix the fields listed in the error response. |
| 429 | Rate limit exceeded | Wait for the retryAfter duration, then retry. |
| 500 | Internal server error | Contact support with the requestId if persistent. |
| 502 | Bad gateway — upstream QuickBooks communication failed | Check that QuickBooks Desktop is running and the Web Connector is connected. |
| 503 | Service unavailable — QuickBooks connection is offline | Verify that QuickBooks Desktop is open and the Web Connector is running. |
Rate limiting
The API enforces per-IP rate limits to protect service stability.
| Scope | Limit | Window |
|---|---|---|
| API endpoints | 100 requests | 1 minute (fixed) |
| Authentication endpoints | 10 requests | 1 minute (fixed) |
| Login attempts | 5 attempts | 15 minutes (sliding) |
When you exceed a limit, the API returns a 429 with a retryAfter field indicating how many seconds to wait before retrying.
{
"error": {
"message": "Rate limit exceeded",
"userFacingMessage": "Too many requests. Please try again later.",
"type": "RATE_LIMIT_ERROR_TYPE",
"code": "RATE_LIMIT_EXCEEDED",
"httpStatusCode": 429,
"integrationCode": null,
"requestId": "req_def456"
}
}What to do: Wait for the number of seconds indicated by retryAfter, then retry the request.
Input sanitization
The nXus API sanitizes all string values before sending them to QuickBooks Desktop. This means the value you submit may differ slightly from what QuickBooks stores.
What the API does:
- Unicode normalization — accented characters are decomposed to their base form (e.g.
café→cafe,müller→muller). - Smart quote replacement — typographic quotes and dashes are converted to their ASCII equivalents (e.g.
"word"→"word",—→-). - Non-printable character removal — control characters, zero-width characters, and non-ASCII symbols are stripped.
- Non-Latin script removal — characters outside printable ASCII (0x20–0x7E) are dropped after normalization.
Why this matters: If your application stores the original input and compares it against the value returned from QuickBooks, you may see mismatches. Always use the value returned by the API as the source of truth after a create or update operation.
Affected fields: All user-supplied text fields — names, addresses, memos, notes, descriptions, and custom field values.
Handling patterns
Display user-facing messages
Always prefer userMessage (or userFacingMessage from the raw response). It’s written for non-technical operators, while message may include internal details like QuickBooks XML status codes.
const err = NxusApiError.from(error);
showToast(err.userMessage);Map validation errors to form fields
const err = NxusApiError.from(error);
if (err.isValidationError && err.validationErrors) {
for (const [field, messages] of Object.entries(err.validationErrors)) {
const input = document.querySelector(`[name="${field}"]`);
if (input) input.classList.add('error');
console.warn(`${field}: ${messages[0]}`);
}
}What to do for each error
nXus errors are not transient — they require action to resolve. The API communicates synchronously with QuickBooks Desktop through the Web Connector, so retrying the same request won’t produce a different result.
| Error | Action |
|---|---|
| 401, 403 — Authentication/authorization | Fix credentials or permissions. |
| 402 — Payment required | Complete billing setup. |
| 404 — Not found | Verify the resource ID. |
| 409 — Stale edit sequence | Re-fetch the record to get the current editSequence, then resubmit. |
| 422 — Validation | Fix the invalid fields. |
| 429 — Rate limit | Wait for the retryAfter duration, then send the request again. |
| 500 — Internal error | Contact support with the requestId. |
| 502, 503 — QuickBooks offline | Verify that QuickBooks Desktop is open, the company file is loaded, and the Web Connector is running. |
Include requestId in support tickets
Every error response includes a requestId (e.g. req_abc123). When contacting support, always include this value — it lets us trace the exact request through our logs.
QuickBooks-specific errors
integrationCode
When an error originates from QuickBooks Desktop, the integrationCode field contains the QuickBooks-specific error identifier — either an HRESULT hex code (e.g. 0x80040408) for COM connection failures or a QBXML status code (e.g. 3100) for business logic errors.
This field is null for errors that don’t involve QuickBooks (authentication, validation, rate limiting, billing).
const err = NxusApiError.from(error);
if (err.isIntegrationError) {
console.log(`QuickBooks error: ${err.integrationCode}`);
// Show the user-safe message, not the raw QB code
showToast(err.userMessage);
}Stale edit sequence (409)
QuickBooks uses an editSequence field for optimistic concurrency. If you try to update a record that was modified since you last read it, you’ll receive:
{
"error": {
"message": "The edit sequence is stale. The record was modified since it was last read.",
"userFacingMessage": "This record was modified by another user. Please refresh and try again.",
"type": "INTEGRATION_RESPONSE_ERROR_TYPE",
"code": "QBD_STALE_EDIT_SEQUENCE",
"httpStatusCode": 409,
"integrationCode": null,
"requestId": "req_xyz789"
}
}Resolution: Re-fetch the record to get the current editSequence, then retry the update with the fresh value.
Connection errors (502, 503)
These indicate that QuickBooks Desktop is unreachable — either the application is closed, the Web Connector isn’t running, or the desktop machine is offline.
- 502 — the API reached the connector but QuickBooks didn’t respond correctly.
- 503 — the connection is completely unavailable.
Resolution: Verify that QuickBooks Desktop is open, the company file is loaded, and the Web Connector service is running.
Common failure patterns
Sync actions fail with permission errors
The dedicated QuickBooks user may not have enough access for the operation being attempted. Review the assigned permissions and compare them against the QuickBooks actions your integration needs to perform.
The wrong user is selected in QuickBooks preferences
Ensure the QuickBooks Web Connector is configured to use the dedicated integration user, not an administrator or personal account.
An operation works as Admin but fails through the API
That usually means the dedicated integration user does not have the same QuickBooks permissions as the Admin account. Increase the user’s QuickBooks permissions or use a role with the necessary rights.
Testing your error handling
Simulate authentication failures
- Use an invalid or expired API key
- Remove the
Authorizationheader entirely - Test with a revoked key if possible
Simulate validation failures
- Send requests with missing required fields
- Include invalid data types (string where number expected)
- Send malformed JSON or invalid enum values
- Omit the
Content-Type: application/jsonheader
Simulate connection failures
- Temporarily disconnect the machine running QuickBooks
- Close QuickBooks Desktop while leaving the Web Connector running
- Stop the Web Connector service
Simulate rate limiting
- Send rapid bursts of requests from a test script
- Verify your backoff logic waits for
retryAfterbefore retrying
Key principles
- Always show something — never swallow errors silently.
- Prefer
userMessage— it’s written for operators, not developers. - Log
requestId— it’s your fastest path to support resolution. - Don’t retry blindly — nXus errors are not transient. Fix the underlying issue (credentials, payload, QuickBooks environment) before sending the request again.
- Expect sanitized values — strings returned from QuickBooks may differ from what you submitted.
Further reading
- Error Responses — Full error type and code reference tables
- Quickstart — Get up and running in minutes
- API Reference — Explore the nXus API
- Pagination — Learn how to handle large data sets