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-response.json
1234567891011
{
"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"
}
}
FieldPurpose
messageDeveloper-facing detail. May include QuickBooks-specific codes or internal context.
userFacingMessageSafe to display to end users — written for non-technical operators.
typeBroad error category (e.g. AUTHENTICATION_ERROR_TYPE, RATE_LIMIT_ERROR_TYPE).
codeSpecific error code for programmatic handling (e.g. API_KEY_INVALID, QBD_CONNECTION_ERROR).
httpStatusCodeThe HTTP status code.
integrationCodeQuickBooks-specific detail when available — an HRESULT hex code or QBXML status code. null for non-QB errors.
requestIdUnique 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.

index.ts
1
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:

index.ts
1234567891011
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:

index.ts
123456789101112
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:

index.ts
12345678910111213141516171819202122232425262728293031
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}`);
}
TypeScriptPythonMatches
isAuthErroris_auth_error401, 403, or AUTHENTICATION_ERROR_TYPE
isValidationErroris_validation_error422 with per-field validation errors
isRateLimitedis_rate_limited429 or RATE_LIMIT_EXCEEDED code
isConflictis_conflict409 or QBD_STALE_EDIT_SEQUENCE code
isNotFoundis_not_found404 or NOT_FOUND_ERROR_TYPE
isIntegrationErroris_integration_errorHas a QuickBooks integrationCode

Status code reference

StatusMeaningWhat to do
400Bad request — malformed JSON or invalid argumentFix the request payload.
401Unauthorized — missing, invalid, expired, or revoked API keyCheck the Authorization header and rotate the key if needed.
402Payment required — no active subscription for production modeComplete billing setup, then retry the request.
403Forbidden — valid key but insufficient permissionsVerify the key has access to the requested resource.
404Not found — the QuickBooks object doesn’t existVerify the ID or list ID.
405Method not allowedCheck the HTTP method against the endpoint docs.
408Request timeout — the operation exceeded the time limitVerify QuickBooks Desktop is responsive. The COM call already failed.
409Conflict — stale edit sequenceRe-fetch the record, get the current editSequence, and retry the update.
422Validation failed — one or more fields are invalidFix the fields listed in the error response.
429Rate limit exceededWait for the retryAfter duration, then retry.
500Internal server errorContact support with the requestId if persistent.
502Bad gateway — upstream QuickBooks communication failedCheck that QuickBooks Desktop is running and the Web Connector is connected.
503Service unavailable — QuickBooks connection is offlineVerify that QuickBooks Desktop is open and the Web Connector is running.

Rate limiting

The API enforces per-IP rate limits to protect service stability.

ScopeLimitWindow
API endpoints100 requests1 minute (fixed)
Authentication endpoints10 requests1 minute (fixed)
Login attempts5 attempts15 minutes (sliding)

When you exceed a limit, the API returns a 429 with a retryAfter field indicating how many seconds to wait before retrying.

rate-limit-error.json
1234567891011
{
"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:

  1. Unicode normalization — accented characters are decomposed to their base form (e.g. cafécafe, müllermuller).
  2. Smart quote replacement — typographic quotes and dashes are converted to their ASCII equivalents (e.g. "word""word", -).
  3. Non-printable character removal — control characters, zero-width characters, and non-ASCII symbols are stripped.
  4. 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.

index.ts
12
const err = NxusApiError.from(error);
showToast(err.userMessage);

Map validation errors to form fields

index.ts
123456789
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.

ErrorAction
401, 403 — Authentication/authorizationFix credentials or permissions.
402 — Payment requiredComplete billing setup.
404 — Not foundVerify the resource ID.
409 — Stale edit sequenceRe-fetch the record to get the current editSequence, then resubmit.
422 — ValidationFix the invalid fields.
429 — Rate limitWait for the retryAfter duration, then send the request again.
500 — Internal errorContact support with the requestId.
502, 503 — QuickBooks offlineVerify 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).

index.ts
1234567
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:

stale-edit-sequence.json
1234567891011
{
"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 Authorization header 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/json header

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 retryAfter before 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