Errors

Full reference of every error class thrown by Reader.

All errors extend the base ReaderError class and carry a typed code, a retryable flag, and a toJSON() method for structured logging.

import { ReaderError } from "@vakra-dev/reader";

try {
  await reader.scrape({ urls: ["https://example.com"] });
} catch (err) {
  if (err instanceof ReaderError) {
    console.error({
      code: err.code,
      message: err.message,
      retryable: err.retryable,
      url: err.url,
    });
  }
}

Base class

class ReaderError extends Error {
  code: ReaderErrorCode;
  url?: string;
  retryable: boolean;
  cause?: Error;
  toJSON(): SerializedError;
}

Every error in the table below extends ReaderError.

Error reference

Error classCodeRetryableWhen it's thrown
NetworkErrorNETWORK_ERROR✅Connection reset, socket error, network unreachable
TimeoutErrorTIMEOUT✅Request or page load exceeded timeoutMs
DNSErrorDNS_ERROR❌Cannot resolve hostname
TLSErrorTLS_ERROR✅SSL/certificate handshake failed
CloudflareErrorCLOUDFLARE_CHALLENGE✅Cloudflare challenge didn't resolve in time
BotDetectedErrorBOT_DETECTED✅Bot detection page detected in response
AccessDeniedErrorACCESS_DENIED❌401/403 returned by the origin
ProxyConnectionErrorPROXY_CONNECTION_ERROR✅Proxy unreachable or auth failed
ProxyExhaustedErrorPROXY_EXHAUSTED❌All proxy tiers tried and failed
ContentExtractionErrorCONTENT_EXTRACTION_FAILED❌HTML parsing failed - likely corrupt response
EmptyContentErrorEMPTY_CONTENT✅Content below minimum length (50 chars) - could be rate limit
ContentTooLargeErrorCONTENT_TOO_LARGE❌HTML exceeds maximum size limit
MarkdownConversionErrorMARKDOWN_CONVERSION_FAILED❌supermarkdown couldn't convert the HTML
InvalidUrlErrorINVALID_URL❌URL parsing failed
ValidationErrorINVALID_OPTIONS❌Invalid options passed to scrape/crawl
RobotsBlockedErrorROBOTS_BLOCKED❌URL blocked by robots.txt
BrowserPoolErrorBROWSER_ERROR✅Pool initialization or instance failure
ClientClosedErrorCLIENT_CLOSED❌Client has already been closed
NotInitializedErrorNOT_INITIALIZED❌Internal - component not initialized (bug report this)
RetryBudgetExhaustedErrorRETRY_BUDGET_EXHAUSTED❌All retries (engine switch, proxy tier fallback, general) exhausted

Importing specific error classes

import {
  ReaderError,
  NetworkError,
  TimeoutError,
  CloudflareError,
  BotDetectedError,
  AccessDeniedError,
  ProxyConnectionError,
  ProxyExhaustedError,
  RobotsBlockedError,
  ValidationError,
  InvalidUrlError,
  RetryBudgetExhaustedError,
} from "@vakra-dev/reader";

Serialized error format

Every ReaderError instance has a toJSON() method for structured logging:

interface SerializedError {
  name: string;
  code: ReaderErrorCode;
  message: string;
  url?: string;
  timestamp: string;    // ISO timestamp
  retryable: boolean;
  cause?: string;       // Original error message if wrapped
  stack?: string;
  // ... error-specific fields (timeoutMs, challengeType, etc.)
}

Use it to ship errors to Datadog, Sentry, or whatever observability stack you're running:

try {
  await reader.scrape({ urls: [...] });
} catch (err) {
  if (err instanceof ReaderError) {
    logger.error(err.toJSON());
  } else {
    logger.error({ message: err.message, stack: err.stack });
  }
}

Retry pattern using the flag

async function scrapeWithRetry(url, maxAttempts = 3) {
  for (let attempt = 0; attempt < maxAttempts; attempt++) {
    try {
      return await reader.scrape({ urls: [url] });
    } catch (err) {
      if (!(err instanceof ReaderError) || !err.retryable || attempt === maxAttempts - 1) {
        throw err;
      }
      await new Promise(r => setTimeout(r, 1000 * Math.pow(2, attempt)));
    }
  }
}

Where to go next