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
The HTTP API can return a StandardErrorResponse envelope or problem details.
Public resource clients normalize supported responses and throw NxusApiError
in TypeScript/Python or NxusApiException in .NET. Successful lists return a
page; other CRUD methods return the resource or acknowledgement directly.
There is no { data, error } result to inspect after a public resource call.
{
"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"
}
}Raw HTTP fields and normalized SDK properties have different names:
| Raw HTTP field | TypeScript | Python | .NET |
|---|---|---|---|
error.userFacingMessage | userMessage | user_message | UserMessage |
error.httpStatusCode | status | status | Status |
error.code | code | code | Code |
error.type | type | type | ErrorType |
error.requestId | requestId | request_id | RequestId |
error.integrationCode | integrationCode | integration_code | IntegrationCode |
Validation errors map | validationErrors | validation_errors | ValidationErrors |
| Retry delay when supplied | retryAfter | retry_after | RetryAfter |
Optional fields can be absent. Retry delays are in seconds. Raw problem
details use status, detail, and title; validation details can accompany
400 or 422 responses. See the Error Responses reference
for the wire contract and enum values pinned to SDK v1.0.
Using NxusApiError
Configure the client with your API key and select the company on each request. Catch typed errors at your application boundary. Rethrow unexpected programming or argument errors rather than swallowing them.
import { NxusClient, NxusApiError } from 'nxus-qbd';
const apiKey = process.env.NXUS_API_KEY;
if (!apiKey) throw new Error("Set NXUS_API_KEY");
const nxus = new NxusClient({ apiKey });
try {
const page = await nxus.vendors.list(
{ limit: 50 }, { connectionId: "YOUR_CONNECTION_ID" },
);
console.log(page.data);
} catch (err) {
if (!(err instanceof NxusApiError)) throw err;
console.error(err.userMessage);
console.error({ code: err.code, status: err.status, requestId: err.requestId });
if (err.isValidationError) console.error(err.validationErrors);
if (err.isConnectorOffline) console.error("Reconnect QuickBooks Web Connector.");
if (err.isRestrictionError) console.error("Resolve connection or billing restrictions.");
if (err.isRateLimited) console.error("Retry delay in seconds:", err.retryAfter);
if (err.code === "QBD_STALE_EDIT_SEQUENCE") {
console.error("Fetch the current revisionNumber before updating.");
}
}Boolean helpers
These are properties, not methods. Classifications can overlap; inspect the specific code before choosing a recovery action.
| TypeScript | Python | .NET | Meaning |
|---|---|---|---|
isAuthError | is_auth_error | IsAuthError | Authentication/authorization failure |
isValidationError | is_validation_error | IsValidationError | Validation code/type or field errors; not every 400/422 |
isRateLimited | is_rate_limited | IsRateLimited | 429 or RATE_LIMIT_EXCEEDED |
isConnectorOffline | is_connector_offline | IsConnectorOffline | QWC_NOT_CONNECTED, QWC_NEVER_CONNECTED, or offline type |
isRestrictionError | is_restriction_error | IsRestrictionError | Lifecycle/billing restriction |
isConflict | is_conflict | IsConflict | Any 409 or QBD_STALE_EDIT_SEQUENCE; not a blanket retry signal |
isNotFound | is_not_found | IsNotFound | Missing resource |
isIntegrationError | is_integration_error | IsIntegrationError | QuickBooks integration code is present |
Status code reference
| Status | Action |
|---|---|
| 400, 422 | Correct invalid input and inspect the field errors. |
| 401, 403 | Check credentials and permissions. |
| 402 | Resolve billing or the reported lifecycle restriction. |
| 404 | Verify the resource ID and target company. |
| 405 | Check the endpoint’s HTTP method. |
| 408, 504 | Preserve an uncertain Create’s identity; it may still have completed. |
| 409 | Inspect the code and retry signal. Stale revisions and idempotency-key reuse require different fixes. |
| 429 | Follow the server delay and bounded retry policy. |
| 500, 502, 503 | Inspect the code, connector status, and request ID. Resolve offline/restriction conditions before resuming. |
Rate limiting
Limits vary by endpoint and deployment. A 429 response can supply Retry-After
and/or a supported body delay field. The SDK normalizes that guidance into its
retry-delay property when available.
Recommended retry pattern
The SDK already performs bounded automatic retries. Its default budget is two
retries (up to three total attempts for an eligible initial request). Configure
this budget instead of adding a second automatic loop around it. The transport
honors x-should-retry and server delay guidance. Long delays can be surfaced
for application scheduling rather than waited out within the SDK.
import { NxusApiError, NxusClient } from "nxus-qbd";
const apiKey = process.env.NXUS_API_KEY;
if (!apiKey) throw new Error("Set NXUS_API_KEY before running this example.");
const client = new NxusClient({ apiKey });
try {
const page = await client.vendors.list(
{ limit: 5 },
{ connectionId: "conn_abc123", maxRetries: 2 }, // three attempts total
);
console.log("Fetched " + page.data.length + " vendors.");
} catch (error) {
if (error instanceof NxusApiError && error.isRateLimited) {
console.error("Rate limited; retry after " + (error.retryAfter ?? "an unspecified delay") + " seconds.");
} else {
throw error;
}
}Pagination continuation requests and Delete/void operations default to no retries. If a continuation fails, discard the cursor and restart from page 1 instead of blindly repeating a request against that stateful cursor.
Recovering Create requests
Supported generic Create methods attach a stable Idempotency-Key across
automatic attempts. Persist your own key, connection ID, and unchanged payload
when a workflow can resume later. Protected Creates can also retry local HTTP
timeouts within their budget; this does not apply to every POST.
A timeout does not prove that QuickBooks failed to create the record. Reusing
the key with a changed payload can produce 409 IDEMPOTENCY_KEY_REUSED, which
is different from QBD_STALE_EDIT_SEQUENCE. See Error Recovery
for supported operations and recovery limits. HTTP timeout and server queue
timeout are distinct budgets; see the Latency Model.
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 the normalized user-facing message and retain the request ID, status, code, and developer message in diagnostic logs. Map validation field keys to your form inputs when details are available. Field names can follow the server’s request naming conventions.
QuickBooks-specific errors
integrationCode
This optional field contains an HRESULT or qbXML status detail when the failure originates in QuickBooks. Connector-offline and policy failures can still be actionable without it. Display the normalized user-facing message and inspect the specific error classification.
Stale edit sequence (409)
QuickBooks uses an edit sequence internally. The public SDK exposes it as
revisionNumber (Python: revision_number). For QBD_STALE_EDIT_SEQUENCE,
retrieve the record again and submit its current revision number with the update.
Other 409 errors can represent business conflicts or idempotency-key misuse;
isConflict alone does not authorize retrying them.
Testing your error handling
Use a test connection to check credential, validation, connector-offline, and restriction messages. Use mocked responses for repeatable retry-delay tests without generating bursts of live traffic. Verify that unknown errors propagate and uncertain Creates retain their original identity.
Further reading
- Error Recovery — recover supported Creates without duplicates
- Error Responses — wire fields and enum reference
- Quickstart — client setup and first requests
- Pagination — page navigation and cursor lifetime