Error Handling
Build robust TypeScript applications with the Tabstack SDK's comprehensive error class hierarchy and handling patterns.
Building robust applications requires proper error handling. The Tabstack SDK provides a comprehensive error class hierarchy and patterns for handling different error scenarios.
Error Class Hierarchy
Section titled “Error Class Hierarchy”The SDK includes specific error classes for different failure scenarios:
TabstackError (base class)├── APIError (with status code)│ ├── AuthenticationError (401)│ ├── PermissionDeniedError (403)│ ├── NotFoundError (404)│ ├── ConflictError (409)│ ├── UnprocessableEntityError (422)│ ├── RateLimitError (429)│ ├── InternalServerError (500+)│ └── BadRequestError (400)├── APIConnectionError└── APIConnectionTimeoutErrorImporting Error Classes
Section titled “Importing Error Classes”import Tabstack, { TabstackError, APIError, AuthenticationError, PermissionDeniedError, NotFoundError, ConflictError, UnprocessableEntityError, RateLimitError, InternalServerError, BadRequestError, APIConnectionError, APIConnectionTimeoutError} from '@tabstack/sdk';const Tabstack = require('@tabstack/sdk').default;const { TabstackError, APIError, AuthenticationError, PermissionDeniedError, NotFoundError, ConflictError, UnprocessableEntityError, RateLimitError, InternalServerError, BadRequestError, APIConnectionError, APIConnectionTimeoutError} = require('@tabstack/sdk');Error Classes Reference
Section titled “Error Classes Reference”TabstackError
Section titled “TabstackError”Base error class for all SDK errors. All other error classes extend this.
Properties:
message: string- Error messagename: string- Error class namecause?: Error- Optional underlying error
APIError
Section titled “APIError”Generic API error with HTTP status code. Used for errors that don’t match specific error classes.
Properties:
status: number- HTTP status codemessage: string- Error message
AuthenticationError (401)
Section titled “AuthenticationError (401)”Thrown when authentication fails.
Common causes:
- Invalid API key
- Missing API key
- Expired API key
PermissionDeniedError (403)
Section titled “PermissionDeniedError (403)”Thrown when you don’t have permission to access a resource.
Common causes:
- Insufficient permissions
- Resource access denied
NotFoundError (404)
Section titled “NotFoundError (404)”Thrown when a requested resource is not found.
Common causes:
- Invalid endpoint
- Resource doesn’t exist
ConflictError (409)
Section titled “ConflictError (409)”Thrown when there’s a conflict with the current state of a resource.
Common causes:
- Duplicate resource creation
- Version conflicts
UnprocessableEntityError (422)
Section titled “UnprocessableEntityError (422)”Thrown when the provided data is invalid or inaccessible.
Common causes:
- Malformed URL format
- URL points to private/internal resources (localhost, 127.0.0.1, private IPs)
- URL is unreachable
- Invalid JSON schema format
RateLimitError (429)
Section titled “RateLimitError (429)”Thrown when you’ve exceeded the rate limit.
Common causes:
- Too many requests in a short period
- Quota exceeded
InternalServerError (500+)
Section titled “InternalServerError (500+)”Thrown when the server encounters an internal error.
Common causes:
- Failed to fetch the URL
- Page content too large
- Failed to generate/extract data
- Server-side processing error
BadRequestError (400)
Section titled “BadRequestError (400)”Thrown when the request is malformed or invalid.
Common causes:
- Missing required parameters
- Invalid JSON schema format
- Malformed request body
APIConnectionError
Section titled “APIConnectionError”Thrown when the SDK cannot connect to the API.
Common causes:
- Network connectivity issues
- DNS resolution failures
- Firewall blocking requests
APIConnectionTimeoutError
Section titled “APIConnectionTimeoutError”Thrown when the connection times out.
Common causes:
- Slow network connection
- Server not responding
- Request took too long
Basic Error Handling
Section titled “Basic Error Handling”Simple Try-Catch
Section titled “Simple Try-Catch”import Tabstack, { TabstackError } from '@tabstack/sdk';
const client = new Tabstack({ apiKey: process.env.TABSTACK_API_KEY!});
try { const result = await client.extract.markdown({ url: 'https://example.com' }); console.log(result.content);} catch (error) { if (error instanceof TabstackError) { console.error('API error:', error.message); } else { console.error('Unexpected error:', error); }}const Tabstack = require('@tabstack/sdk').default;const { TabstackError } = require('@tabstack/sdk');
const client = new Tabstack({ apiKey: process.env.TABSTACK_API_KEY});
try { const result = await client.extract.markdown({ url: 'https://example.com' }); console.log(result.content);} catch (error) { if (error instanceof TabstackError) { console.error('API error:', error.message); } else { console.error('Unexpected error:', error); }}Handling Specific Error Types
Section titled “Handling Specific Error Types”import Tabstack, { AuthenticationError, UnprocessableEntityError, InternalServerError, RateLimitError, TabstackError} from '@tabstack/sdk';
const client = new Tabstack({ apiKey: process.env.TABSTACK_API_KEY!});
async function extractWithErrorHandling(url: string) { try { const result = await client.extract.markdown({ url }); return result; } catch (error) { if (error instanceof AuthenticationError) { console.error('Authentication failed. Please check your API key.'); } else if (error instanceof UnprocessableEntityError) { console.error(`Invalid or inaccessible URL: ${url}`); } else if (error instanceof RateLimitError) { console.error('Rate limit exceeded. Waiting before retry...'); await new Promise(resolve => setTimeout(resolve, 60000)); return extractWithErrorHandling(url); } else if (error instanceof InternalServerError) { console.error('Server error occurred. Retrying in 5 seconds...'); await new Promise(resolve => setTimeout(resolve, 5000)); return extractWithErrorHandling(url); } else if (error instanceof TabstackError) { console.error('API error:', error.message); } else { console.error('Unexpected error:', error); throw error; } }}const Tabstack = require('@tabstack/sdk').default;const { AuthenticationError, UnprocessableEntityError, InternalServerError, RateLimitError, TabstackError} = require('@tabstack/sdk');
const client = new Tabstack({ apiKey: process.env.TABSTACK_API_KEY});
async function extractWithErrorHandling(url) { try { const result = await client.extract.markdown({ url }); return result; } catch (error) { if (error instanceof AuthenticationError) { console.error('Authentication failed. Please check your API key.'); } else if (error instanceof UnprocessableEntityError) { console.error(`Invalid or inaccessible URL: ${url}`); } else if (error instanceof RateLimitError) { console.error('Rate limit exceeded. Waiting before retry...'); await new Promise(resolve => setTimeout(resolve, 60000)); return extractWithErrorHandling(url); } else if (error instanceof InternalServerError) { console.error('Server error occurred. Retrying in 5 seconds...'); await new Promise(resolve => setTimeout(resolve, 5000)); return extractWithErrorHandling(url); } else if (error instanceof TabstackError) { console.error('API error:', error.message); } else { console.error('Unexpected error:', error); throw error; } }}Advanced Error Handling Patterns
Section titled “Advanced Error Handling Patterns”Retry Logic with Exponential Backoff
Section titled “Retry Logic with Exponential Backoff”import Tabstack, { InternalServerError, RateLimitError } from '@tabstack/sdk';
async function extractWithRetry( client: Tabstack, url: string, maxRetries: number = 3, baseDelay: number = 1000) { let lastError: Error;
for (let attempt = 0; attempt < maxRetries; attempt++) { try { const result = await client.extract.markdown({ url }); return result; } catch (error) { lastError = error;
if (error instanceof InternalServerError || error instanceof RateLimitError) { if (attempt < maxRetries - 1) { const delay = baseDelay * Math.pow(2, attempt); console.log(`Attempt ${attempt + 1} failed. Retrying in ${delay}ms...`); await new Promise(resolve => setTimeout(resolve, delay)); continue; } }
throw error; } }
throw new Error(`Failed after ${maxRetries} attempts: ${lastError.message}`);}
// Usageconst client = new Tabstack({ apiKey: process.env.TABSTACK_API_KEY! });
try { const result = await extractWithRetry(client, 'https://example.com'); console.log('Success:', result.content);} catch (error) { console.error('Failed:', error.message);}const Tabstack = require('@tabstack/sdk').default;const { InternalServerError, RateLimitError } = require('@tabstack/sdk');
async function extractWithRetry( client, url, maxRetries = 3, baseDelay = 1000) { let lastError;
for (let attempt = 0; attempt < maxRetries; attempt++) { try { const result = await client.extract.markdown({ url }); return result; } catch (error) { lastError = error;
if (error instanceof InternalServerError || error instanceof RateLimitError) { if (attempt < maxRetries - 1) { const delay = baseDelay * Math.pow(2, attempt); console.log(`Attempt ${attempt + 1} failed. Retrying in ${delay}ms...`); await new Promise(resolve => setTimeout(resolve, delay)); continue; } }
throw error; } }
throw new Error(`Failed after ${maxRetries} attempts: ${lastError.message}`);}
// Usageconst client = new Tabstack({ apiKey: process.env.TABSTACK_API_KEY });
try { const result = await extractWithRetry(client, 'https://example.com'); console.log('Success:', result.content);} catch (error) { console.error('Failed:', error.message);}Batch Processing with Error Tracking
Section titled “Batch Processing with Error Tracking”import Tabstack, { TabstackError } from '@tabstack/sdk';
interface BatchResult<T> { url: string; success: boolean; data?: T; error?: string;}
async function batchExtractWithErrorHandling<T>( client: Tabstack, urls: string[], schema: object): Promise<BatchResult<T>[]> { const results: BatchResult<T>[] = [];
for (const url of urls) { try { const result = await client.extract.json({ url, json_schema: schema }); results.push({ url, success: true, data: result as T }); console.log(`Success: ${url}`); } catch (error) { const errorMessage = error instanceof TabstackError ? error.message : 'Unknown error';
results.push({ url, success: false, error: errorMessage }); console.error(`Failed: ${url} - ${errorMessage}`); }
// Respectful delay between requests await new Promise(resolve => setTimeout(resolve, 500)); }
return results;}
// Usageconst client = new Tabstack({ apiKey: process.env.TABSTACK_API_KEY! });const urls = [ 'https://example.com/page1', 'https://example.com/page2', 'https://example.com/page3'];
const schema = { type: 'object', properties: { title: { type: 'string' } }};
const results = await batchExtractWithErrorHandling(client, urls, schema);
const successful = results.filter(r => r.success);const failed = results.filter(r => !r.success);
console.log(`\nBatch Complete: ${successful.length} succeeded, ${failed.length} failed`);
if (failed.length > 0) { console.log('\nFailed URLs:'); failed.forEach(r => console.log(` - ${r.url}: ${r.error}`));}const Tabstack = require('@tabstack/sdk').default;const { TabstackError } = require('@tabstack/sdk');
async function batchExtractWithErrorHandling(client, urls, schema) { const results = [];
for (const url of urls) { try { const result = await client.extract.json({ url, json_schema: schema }); results.push({ url, success: true, data: result }); console.log(`Success: ${url}`); } catch (error) { const errorMessage = error instanceof TabstackError ? error.message : 'Unknown error';
results.push({ url, success: false, error: errorMessage }); console.error(`Failed: ${url} - ${errorMessage}`); }
// Respectful delay between requests await new Promise(resolve => setTimeout(resolve, 500)); }
return results;}
// Usageconst client = new Tabstack({ apiKey: process.env.TABSTACK_API_KEY });const urls = [ 'https://example.com/page1', 'https://example.com/page2', 'https://example.com/page3'];
const schema = { type: 'object', properties: { title: { type: 'string' } }};
const results = await batchExtractWithErrorHandling(client, urls, schema);
const successful = results.filter(r => r.success);const failed = results.filter(r => !r.success);
console.log(`\nBatch Complete: ${successful.length} succeeded, ${failed.length} failed`);
if (failed.length > 0) { console.log('\nFailed URLs:'); failed.forEach(r => console.log(` - ${r.url}: ${r.error}`));}Error Logging and Monitoring
Section titled “Error Logging and Monitoring”import Tabstack, { TabstackError, APIError } from '@tabstack/sdk';
interface ErrorLog { timestamp: Date; operation: string; url?: string; errorType: string; message: string; status?: number; stack?: string;}
class ErrorTracker { private errors: ErrorLog[] = [];
log(operation: string, error: Error, url?: string) { const errorLog: ErrorLog = { timestamp: new Date(), operation, url, errorType: error.constructor.name, message: error.message, stack: error.stack };
if (error instanceof APIError) { errorLog.status = error.status; }
this.errors.push(errorLog); console.error(`[${errorLog.timestamp.toISOString()}] ${operation} failed:`, error.message); }
getErrors() { return this.errors; }
getSummary() { const byType = this.errors.reduce((acc, error) => { acc[error.errorType] = (acc[error.errorType] || 0) + 1; return acc; }, {} as Record<string, number>);
return { total: this.errors.length, byType }; }
exportToJSON() { return JSON.stringify(this.errors, null, 2); }}
// Usageconst client = new Tabstack({ apiKey: process.env.TABSTACK_API_KEY! });const errorTracker = new ErrorTracker();
async function monitoredExtract(url: string) { try { return await client.extract.markdown({ url }); } catch (error) { errorTracker.log('extract.markdown', error, url); throw error; }}
const urls = ['https://example.com/page1', 'https://example.com/page2'];
for (const url of urls) { try { await monitoredExtract(url); } catch (error) { // Error already logged, continue processing }}
console.log('\nError Summary:', errorTracker.getSummary());const Tabstack = require('@tabstack/sdk').default;const { APIError } = require('@tabstack/sdk');
class ErrorTracker { constructor() { this.errors = []; }
log(operation, error, url) { const errorLog = { timestamp: new Date(), operation, url, errorType: error.constructor.name, message: error.message, stack: error.stack };
if (error instanceof APIError) { errorLog.status = error.status; }
this.errors.push(errorLog); console.error(`[${errorLog.timestamp.toISOString()}] ${operation} failed:`, error.message); }
getErrors() { return this.errors; }
getSummary() { const byType = this.errors.reduce((acc, error) => { acc[error.errorType] = (acc[error.errorType] || 0) + 1; return acc; }, {});
return { total: this.errors.length, byType }; }
exportToJSON() { return JSON.stringify(this.errors, null, 2); }}
// Usageconst client = new Tabstack({ apiKey: process.env.TABSTACK_API_KEY });const errorTracker = new ErrorTracker();
async function monitoredExtract(url) { try { return await client.extract.markdown({ url }); } catch (error) { errorTracker.log('extract.markdown', error, url); throw error; }}
const urls = ['https://example.com/page1', 'https://example.com/page2'];
for (const url of urls) { try { await monitoredExtract(url); } catch (error) { // Error already logged, continue processing }}
console.log('\nError Summary:', errorTracker.getSummary());Automate Error Handling
Section titled “Automate Error Handling”Automate operations stream events and require different error handling:
import Tabstack, { TabstackError } from '@tabstack/sdk';
const client = new Tabstack({ apiKey: process.env.TABSTACK_API_KEY! });
async function automateWithErrorHandling(task: string, url: string) { try { const stream = await client.agent.automate({ task, url, guardrails: 'browse and extract only' });
for await (const event of stream) { if (event.event === 'error') { const error = event.data?.error; console.error('Automation error:', error); throw new Error(`Automation failed: ${error}`); }
if (event.event === 'task:aborted') { const reason = event.data?.reason; console.warn('Task aborted:', reason); throw new Error(`Task aborted: ${reason}`); }
if (event.event === 'task:validation_error') { const error = event.data?.error; console.error('Validation failed:', error); }
if (event.event === 'task:completed') { const result = event.data?.finalAnswer; console.log('Success:', result); return result; } } } catch (error) { if (error instanceof TabstackError) { console.error('API error during automation:', error.message); } else { console.error('Unexpected error:', error); } throw error; }}
// Usagetry { await automateWithErrorHandling( 'Extract the top 5 articles', 'https://news.example.com' );} catch (error) { console.error('Automation failed completely:', error.message);}const Tabstack = require('@tabstack/sdk').default;const { TabstackError } = require('@tabstack/sdk');
const client = new Tabstack({ apiKey: process.env.TABSTACK_API_KEY });
async function automateWithErrorHandling(task, url) { try { const stream = await client.agent.automate({ task, url, guardrails: 'browse and extract only' });
for await (const event of stream) { if (event.event === 'error') { const error = event.data?.error; console.error('Automation error:', error); throw new Error(`Automation failed: ${error}`); }
if (event.event === 'task:aborted') { const reason = event.data?.reason; console.warn('Task aborted:', reason); throw new Error(`Task aborted: ${reason}`); }
if (event.event === 'task:validation_error') { const error = event.data?.error; console.error('Validation failed:', error); }
if (event.event === 'task:completed') { const result = event.data?.finalAnswer; console.log('Success:', result); return result; } } } catch (error) { if (error instanceof TabstackError) { console.error('API error during automation:', error.message); } else { console.error('Unexpected error:', error); } throw error; }}
// Usagetry { await automateWithErrorHandling( 'Extract the top 5 articles', 'https://news.example.com' );} catch (error) { console.error('Automation failed completely:', error.message);}Best Practices
Section titled “Best Practices”1. Always Use Try-Catch
Section titled “1. Always Use Try-Catch”// Bad: No error handlingconst result = await client.extract.markdown({ url });
// Good: Proper error handlingtry { const result = await client.extract.markdown({ url });} catch (error) { // Handle error}2. Handle Specific Errors First
Section titled “2. Handle Specific Errors First”try { // API call} catch (error) { if (error instanceof AuthenticationError) { // Handle auth error } else if (error instanceof UnprocessableEntityError) { // Handle URL error } else if (error instanceof TabstackError) { // Handle other API errors } else { // Handle unexpected errors }}3. Implement Retry Logic for Transient Errors
Section titled “3. Implement Retry Logic for Transient Errors”if (error instanceof InternalServerError || error instanceof RateLimitError) { // Implement retry with backoff} else if (error instanceof AuthenticationError || error instanceof BadRequestError) { // Don't retry - fix the issue first throw error;}4. Log Errors for Debugging
Section titled “4. Log Errors for Debugging”try { const result = await client.extract.json({ url, json_schema: schema });} catch (error) { console.error(`Failed to extract from ${url}:`, { errorType: error.constructor.name, message: error.message, url, timestamp: new Date().toISOString() });}5. Provide User-Friendly Messages
Section titled “5. Provide User-Friendly Messages”try { const result = await client.extract.markdown({ url });} catch (error) { if (error instanceof AuthenticationError) { return 'Your API key is invalid. Please check your credentials and try again.'; } else if (error instanceof UnprocessableEntityError) { return 'The URL you provided cannot be accessed. Please verify it is correct and publicly accessible.'; } else { return 'An unexpected error occurred. Please try again later or contact support.'; }}6. Clean Up Resources on Error
Section titled “6. Clean Up Resources on Error”let logFile;try { logFile = openLog(); const result = await client.extract.markdown({ url }); writeLog(logFile, result);} catch (error) { console.error('Error:', error); throw error;} finally { if (logFile) { closeLog(logFile); }}Common Error Scenarios
Section titled “Common Error Scenarios”Invalid API Key
Section titled “Invalid API Key”try { const result = await client.extract.markdown({ url });} catch (error) { if (error instanceof AuthenticationError) { console.error('Please set a valid API key in TABSTACK_API_KEY environment variable'); process.exit(1); }}URL Cannot Be Accessed
Section titled “URL Cannot Be Accessed”try { const result = await client.extract.markdown({ url: 'https://localhost:3000' });} catch (error) { if (error instanceof UnprocessableEntityError) { console.error('Cannot access private URLs like localhost'); }}Schema Mismatch
Section titled “Schema Mismatch”try { const result = await client.extract.json({ url, json_schema: incompatibleSchema });} catch (error) { if (error instanceof InternalServerError && error.message.includes('failed to generate')) { console.error('Schema does not match page content. Try generating a schema first.'); }}Next Steps
Section titled “Next Steps”- Quickstart: Get started with the SDK
- Generate Features: Discover AI-powered transformations
- Automate Features: Execute browser automation
- REST API Documentation: View REST API error responses