Error Handling
Use instanceof checks against specific error subclasses to handle different failure modes. Order your checks from most specific to least specific, with the base TalonicError as the final catch-all.
import {
TalonicAuthError,
TalonicNotFoundError,
TalonicValidationError,
TalonicRateLimitError,
TalonicServerError,
TalonicNetworkError,
TalonicTimeoutError,
TalonicError,
} from '@talonic/node'
try {
const result = await talonic.extract({
file_path: './invoice.pdf',
schema_id: 'sch_abc123',
})
} catch (err) {
if (err instanceof TalonicAuthError) {
console.error('Authentication failed — check your API key')
} else if (err instanceof TalonicNotFoundError) {
console.error(`Resource not found: ${err.message}`)
} else if (err instanceof TalonicValidationError) {
console.error(`Invalid request: ${err.code} — ${err.message}`)
} else if (err instanceof TalonicRateLimitError) {
console.error(`Rate limited. Resets at ${err.rateLimit.resetAt.toISOString()}`)
} else if (err instanceof TalonicServerError) {
console.error(`Server error (retries exhausted): ${err.code}`)
} else if (err instanceof TalonicTimeoutError) {
console.error(`Timed out after ${err.timeoutMs}ms`)
} else if (err instanceof TalonicNetworkError) {
console.error(`Network failure: ${err.message}`)
} else if (err instanceof TalonicError) {
console.error(`Talonic error: ${err.code} (status ${err.status})`)
}
}TalonicRateLimitError includes a rateLimit object with limit, remaining, and resetAt (Date) for scheduling retries. All errors include requestId for support debugging. The TalonicTimeoutError includes timeoutMs with the configured timeout value that was exceeded, and TalonicNetworkError preserves the original error as cause for inspecting DNS or TCP failures.
The retryable property on every error tells you whether the SDK considers the failure worth retrying. When you catch an error after retries are exhausted, checking retryable helps decide whether to queue the request for later or surface it to the user immediately. Errors with retryable: true that still threw mean the SDK exhausted all maxRetries attempts; errors with retryable: false were thrown on the first attempt without any retry.
import { TalonicError } from '@talonic/node'
async function extractWithLogging(filePath: string, schemaId: string) {
try {
return await talonic.extract({ file_path: filePath, schema_id: schemaId })
} catch (err) {
if (err instanceof TalonicError) {
// Structured log entry safe for metrics and alerting
const logEntry = {
level: 'error',
service: 'extraction-pipeline',
error_code: err.code, // 'invalid_schema', 'rate_limited', etc.
http_status: err.status, // 422, 429, 500, or 0 for transport errors
request_id: err.requestId, // server trace ID
retryable: err.retryable, // was this retried?
error_class: err.name, // 'TalonicValidationError', etc.
message: err.message,
file: filePath,
schema_id: schemaId,
}
console.error(JSON.stringify(logEntry))
}
throw err
}
}For logging and observability, capture err.code, err.status, and err.requestId on every failure. The code is a machine-readable string like "invalid_schema" or "rate_limited" that is safe to use in metrics and alerting rules. The err.name property gives you the class name (e.g. 'TalonicValidationError') which is useful for grouping errors by category in your monitoring dashboard.
import { TalonicRateLimitError, TalonicError } from '@talonic/node'
// Retry wrapper that waits for rate limit reset
async function extractWithRateLimitRetry<T>(
fn: () => Promise<T>,
maxAttempts = 2,
): Promise<T> {
for (let attempt = 0; attempt < maxAttempts; attempt++) {
try {
return await fn()
} catch (err) {
if (err instanceof TalonicRateLimitError && attempt < maxAttempts - 1) {
const waitMs = err.rateLimit.resetAt.getTime() - Date.now()
if (waitMs > 0 && waitMs < 120_000) {
console.log(`Rate limited. Waiting ${Math.ceil(waitMs / 1000)}s...`)
await new Promise(r => setTimeout(r, waitMs + 100))
continue
}
}
throw err
}
}
throw new Error('Unreachable')
}
// Usage
const result = await extractWithRateLimitRetry(() =>
talonic.extract({ file_path: './doc.pdf', schema_id: 'sch_abc123' })
)When building extraction pipelines that process many documents, consider wrapping your calls in a function that distinguishes between recoverable and terminal errors. Errors with retryable: true (rate limits, server errors, network issues, timeouts) may succeed if retried later. Errors with retryable: false (auth, validation, not found) indicate a problem with the request itself and should be logged and skipped. This pattern is especially useful for batch processing where you want to continue with remaining documents rather than failing the entire batch.
maxRetries times.