---
title: Error Handling | Tabstack
description: 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

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
└── APIConnectionTimeoutError
```

## Importing Error Classes

- [TypeScript](#tab-panel-294)
- [JavaScript](#tab-panel-295)

```
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

### TabstackError

Base error class for all SDK errors. All other error classes extend this.

**Properties:**

- `message: string` - Error message
- `name: string` - Error class name
- `cause?: Error` - Optional underlying error

### APIError

Generic API error with HTTP status code. Used for errors that don’t match specific error classes.

**Properties:**

- `status: number` - HTTP status code
- `message: string` - Error message

### AuthenticationError (401)

Thrown when authentication fails.

**Common causes:**

- Invalid API key
- Missing API key
- Expired API key

### PermissionDeniedError (403)

Thrown when you don’t have permission to access a resource.

**Common causes:**

- Insufficient permissions
- Resource access denied

### NotFoundError (404)

Thrown when a requested resource is not found.

**Common causes:**

- Invalid endpoint
- Resource doesn’t exist

### ConflictError (409)

Thrown when there’s a conflict with the current state of a resource.

**Common causes:**

- Duplicate resource creation
- Version conflicts

### 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)

Thrown when you’ve exceeded the rate limit.

**Common causes:**

- Too many requests in a short period
- Quota exceeded

### 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)

Thrown when the request is malformed or invalid.

**Common causes:**

- Missing required parameters
- Invalid JSON schema format
- Malformed request body

### APIConnectionError

Thrown when the SDK cannot connect to the API.

**Common causes:**

- Network connectivity issues
- DNS resolution failures
- Firewall blocking requests

### APIConnectionTimeoutError

Thrown when the connection times out.

**Common causes:**

- Slow network connection
- Server not responding
- Request took too long

## Basic Error Handling

### Simple Try-Catch

- [TypeScript](#tab-panel-296)
- [JavaScript](#tab-panel-297)

```
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

- [TypeScript](#tab-panel-298)
- [JavaScript](#tab-panel-299)

```
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

### Retry Logic with Exponential Backoff

- [TypeScript](#tab-panel-300)
- [JavaScript](#tab-panel-301)

```
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}`);
}


// Usage
const 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}`);
}


// Usage
const 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

- [TypeScript](#tab-panel-302)
- [JavaScript](#tab-panel-303)

```
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;
}


// Usage
const 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;
}


// Usage
const 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

- [TypeScript](#tab-panel-304)
- [JavaScript](#tab-panel-305)

```
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);
  }
}


// Usage
const 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);
  }
}


// Usage
const 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

Automate operations stream events and require different error handling:

- [TypeScript](#tab-panel-306)
- [JavaScript](#tab-panel-307)

```
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;
  }
}


// Usage
try {
  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;
  }
}


// Usage
try {
  await automateWithErrorHandling(
    "Extract the top 5 articles",
    "https://news.example.com",
  );
} catch (error) {
  console.error("Automation failed completely:", error.message);
}
```

## Best Practices

### 1. Always Use Try-Catch

```
// Bad: No error handling
const result = await client.extract.markdown({ url });


// Good: Proper error handling
try {
  const result = await client.extract.markdown({ url });
} catch (error) {
  // Handle error
}
```

### 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

```
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

```
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

```
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

```
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

### 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

```
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

```
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

- **[Quickstart](./quickstart)**: Get started with the SDK
- **[Generate Features](./generate)**: Discover AI-powered transformations
- **[Automate Features](./automate)**: Execute browser automation
- **[REST API Documentation](/api/index.md)**: View REST API error responses
