CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-vercel--blob

The Vercel Blob JavaScript API client for cloud blob storage operations

Pending
Overview
Eval results
Files

errors.mddocs/

Error Handling

Comprehensive error system with specific error types for different failure scenarios and detailed error messages.

Overview

The Vercel Blob library provides a comprehensive error handling system with specific error classes for different types of failures. All errors extend from the base BlobError class and include detailed error messages to help developers understand and handle various failure scenarios.

Error Hierarchy

All blob-related errors inherit from the base BlobError class, which extends the standard JavaScript Error.

class BlobError extends Error {
  constructor(message: string);
}

Error Types

Access and Authentication Errors

BlobAccessError

Thrown when the request lacks proper authentication or authorization.

/**
 * Thrown when access is denied due to authentication or authorization issues
 */
class BlobAccessError extends BlobError {
  constructor();
}

Common Causes:

  • Invalid or missing BLOB_READ_WRITE_TOKEN
  • Expired authentication token
  • Insufficient permissions for the requested operation
  • Invalid client token

Example:

import { put, BlobAccessError } from '@vercel/blob';

try {
  await put('test.txt', 'content', {
    access: 'public',
    token: 'invalid-token',
  });
} catch (error) {
  if (error instanceof BlobAccessError) {
    console.error('Access denied:', error.message);
    // Handle authentication error
  }
}

Resource Errors

BlobNotFoundError

Thrown when attempting to access a blob that doesn't exist.

/**
 * Thrown when a requested blob cannot be found
 */
class BlobNotFoundError extends BlobError {
  constructor();
}

Common Operations:

  • head() on non-existent blob
  • del() on non-existent blob
  • copy() from non-existent source

Example:

import { head, BlobNotFoundError } from '@vercel/blob';

try {
  const metadata = await head('non-existent-file.txt');
} catch (error) {
  if (error instanceof BlobNotFoundError) {
    console.log('File does not exist');
    // Handle missing file
  }
}

BlobStoreNotFoundError

Thrown when the blob store itself cannot be found.

/**
 * Thrown when the blob store cannot be found
 */
class BlobStoreNotFoundError extends BlobError {
  constructor();
}

Common Causes:

  • Incorrect store configuration
  • Store has been deleted
  • Invalid store identifier in token

BlobStoreSuspendedError

Thrown when operations are attempted on a suspended blob store.

/**
 * Thrown when attempting operations on a suspended blob store
 */
class BlobStoreSuspendedError extends BlobError {
  constructor();
}

Common Causes:

  • Account billing issues
  • Terms of service violations
  • Administrative suspension

Content and Validation Errors

BlobContentTypeNotAllowedError

Thrown when uploading content with a disallowed MIME type.

/**
 * Thrown when the content type is not allowed for upload
 */
class BlobContentTypeNotAllowedError extends BlobError {
  constructor(message: string);
}

Common Causes:

  • Uploading executable files
  • Restricted file types based on store configuration
  • Invalid or missing content type

Example:

import { put, BlobContentTypeNotAllowedError } from '@vercel/blob';

try {
  await put('script.exe', executableData, {
    access: 'public',
    contentType: 'application/x-executable',
  });
} catch (error) {
  if (error instanceof BlobContentTypeNotAllowedError) {
    console.error('File type not allowed:', error.message);
    // Handle content type restriction
  }
}

BlobPathnameMismatchError

Thrown when there's a mismatch between expected and actual pathname.

/**
 * Thrown when pathname doesn't match expected format or constraints
 */
class BlobPathnameMismatchError extends BlobError {
  constructor(message: string);
}

Common Causes:

  • Pathname too long (exceeds MAXIMUM_PATHNAME_LENGTH)
  • Invalid characters in pathname
  • Pathname format violations

BlobFileTooLargeError

Thrown when uploading files that exceed size limits.

/**
 * Thrown when file size exceeds the allowed limit
 */
class BlobFileTooLargeError extends BlobError {
  constructor(message: string);
}

Common Causes:

  • Single file exceeds server upload limit (4.5MB on Vercel)
  • Multipart part smaller than minimum size (5MB, except last part)
  • Total file size exceeds maximum (5TB)

Service and Network Errors

BlobServiceNotAvailable

Thrown when the blob service is temporarily unavailable.

/**
 * Thrown when the blob service is temporarily unavailable
 */
class BlobServiceNotAvailable extends BlobError {
  constructor();
}

Common Causes:

  • Service maintenance
  • Temporary outages
  • Network connectivity issues

Handling:

import { put, BlobServiceNotAvailable } from '@vercel/blob';

async function uploadWithRetry(pathname: string, body: any, maxRetries = 3) {
  for (let attempt = 1; attempt <= maxRetries; attempt++) {
    try {
      return await put(pathname, body, { access: 'public' });
    } catch (error) {
      if (error instanceof BlobServiceNotAvailable && attempt < maxRetries) {
        console.log(`Service unavailable, retrying... (${attempt}/${maxRetries})`);
        await new Promise(resolve => setTimeout(resolve, Math.pow(2, attempt) * 1000));
        continue;
      }
      throw error;
    }
  }
}

BlobServiceRateLimited

Thrown when requests exceed the rate limit.

/**
 * Thrown when requests exceed the allowed rate limit
 */
class BlobServiceRateLimited extends BlobError {
  public readonly retryAfter: number;
  constructor(seconds?: number);
}

Handling:

import { put, BlobServiceRateLimited } from '@vercel/blob';

async function uploadWithRateLimit(pathname: string, body: any) {
  try {
    return await put(pathname, body, { access: 'public' });
  } catch (error) {
    if (error instanceof BlobServiceRateLimited) {
      console.log('Rate limited, waiting before retry...');
      await new Promise(resolve => setTimeout(resolve, 5000)); // Wait 5 seconds
      return put(pathname, body, { access: 'public' });
    }
    throw error;
  }
}

Request and Token Errors

BlobRequestAbortedError

Thrown when a request is aborted using an AbortSignal.

/**
 * Thrown when a request is aborted via AbortSignal
 */
class BlobRequestAbortedError extends BlobError {
  constructor();
}

Example:

import { put, BlobRequestAbortedError } from '@vercel/blob';

const controller = new AbortController();

// Cancel upload after 5 seconds
setTimeout(() => controller.abort(), 5000);

try {
  await put('large-file.bin', largeData, {
    access: 'public',
    abortSignal: controller.signal,
  });
} catch (error) {
  if (error instanceof BlobRequestAbortedError) {
    console.log('Upload was cancelled');
  }
}

BlobClientTokenExpiredError

Thrown when a client token has expired.

/**
 * Thrown when a client token has expired
 */
class BlobClientTokenExpiredError extends BlobError {
  constructor();
}

Handling:

import { upload, BlobClientTokenExpiredError } from '@vercel/blob/client';

async function uploadWithTokenRefresh(pathname: string, body: any) {
  try {
    return await upload(pathname, body, {
      access: 'public',
      handleUploadUrl: '/api/upload',
    });
  } catch (error) {
    if (error instanceof BlobClientTokenExpiredError) {
      console.log('Token expired, will request new token automatically');
      // The upload function will automatically retry with a new token
      return upload(pathname, body, {
        access: 'public',
        handleUploadUrl: '/api/upload',
      });
    }
    throw error;
  }
}

Generic Errors

BlobUnknownError

Thrown for unexpected or unhandled error conditions.

/**
 * Thrown for unknown or unexpected error conditions
 */
class BlobUnknownError extends BlobError {
  constructor();
}

Error Handling Patterns

Basic Error Handling

import { 
  put, 
  BlobError, 
  BlobAccessError, 
  BlobNotFoundError,
  BlobFileTooLargeError 
} from '@vercel/blob';

async function handleBlobOperation() {
  try {
    const result = await put('test.txt', 'content', {
      access: 'public',
    });
    return result;
    
  } catch (error) {
    if (error instanceof BlobAccessError) {
      console.error('Authentication failed:', error.message);
      // Redirect to login or refresh token
      
    } else if (error instanceof BlobFileTooLargeError) {
      console.error('File too large:', error.message);
      // Suggest using multipart upload
      
    } else if (error instanceof BlobError) {
      console.error('Blob operation failed:', error.message);
      // Generic blob error handling
      
    } else {
      console.error('Unexpected error:', error);
      // Handle non-blob errors
    }
    
    throw error; // Re-throw if needed
  }
}

Comprehensive Error Handler

import { 
  BlobError,
  BlobAccessError,
  BlobNotFoundError,
  BlobStoreNotFoundError,
  BlobStoreSuspendedError,
  BlobContentTypeNotAllowedError,
  BlobPathnameMismatchError,
  BlobFileTooLargeError,
  BlobServiceNotAvailable,
  BlobServiceRateLimited,
  BlobRequestAbortedError,
  BlobClientTokenExpiredError,
  BlobUnknownError
} from '@vercel/blob';

interface ErrorInfo {
  type: string;
  message: string;
  isRetryable: boolean;
  suggestedAction: string;
}

function analyzeBlobError(error: unknown): ErrorInfo {
  if (!(error instanceof BlobError)) {
    return {
      type: 'UnknownError',
      message: String(error),
      isRetryable: false,
      suggestedAction: 'Check the error details and try again',
    };
  }

  if (error instanceof BlobAccessError) {
    return {
      type: 'AccessError',
      message: error.message,
      isRetryable: false,
      suggestedAction: 'Check authentication tokens and permissions',
    };
  }

  if (error instanceof BlobNotFoundError) {
    return {
      type: 'NotFoundError',
      message: error.message,
      isRetryable: false,
      suggestedAction: 'Verify the blob pathname or URL is correct',
    };
  }

  if (error instanceof BlobStoreNotFoundError) {
    return {
      type: 'StoreNotFoundError',
      message: error.message,
      isRetryable: false,
      suggestedAction: 'Check blob store configuration',
    };
  }

  if (error instanceof BlobStoreSuspendedError) {
    return {
      type: 'StoreSuspendedError',
      message: error.message,
      isRetryable: false,
      suggestedAction: 'Contact support to resolve account issues',
    };
  }

  if (error instanceof BlobContentTypeNotAllowedError) {
    return {
      type: 'ContentTypeError',
      message: error.message,
      isRetryable: false,
      suggestedAction: 'Use an allowed content type or modify store settings',
    };
  }

  if (error instanceof BlobPathnameMismatchError) {
    return {
      type: 'PathnameError',
      message: error.message,
      isRetryable: false,
      suggestedAction: 'Use a valid pathname format',
    };
  }

  if (error instanceof BlobFileTooLargeError) {
    return {
      type: 'FileTooLargeError',
      message: error.message,
      isRetryable: false,
      suggestedAction: 'Use multipart upload for large files',
    };
  }

  if (error instanceof BlobServiceNotAvailable) {
    return {
      type: 'ServiceUnavailableError',
      message: error.message,
      isRetryable: true,
      suggestedAction: 'Retry the operation after a delay',
    };
  }

  if (error instanceof BlobServiceRateLimited) {
    return {
      type: 'RateLimitedError',
      message: error.message,
      isRetryable: true,
      suggestedAction: 'Wait before retrying to respect rate limits',
    };
  }

  if (error instanceof BlobRequestAbortedError) {
    return {
      type: 'RequestAbortedError',
      message: error.message,
      isRetryable: false,
      suggestedAction: 'Restart the operation if needed',
    };
  }

  if (error instanceof BlobClientTokenExpiredError) {
    return {
      type: 'TokenExpiredError',
      message: error.message,
      isRetryable: true,
      suggestedAction: 'The operation will automatically retry with a new token',
    };
  }

  if (error instanceof BlobUnknownError) {
    return {
      type: 'UnknownBlobError',
      message: error.message,
      isRetryable: false,
      suggestedAction: 'Check the error details and contact support if needed',
    };
  }

  return {
    type: 'GenericBlobError',
    message: error.message,
    isRetryable: false,
    suggestedAction: 'Review the error and try again',
  };
}

// Usage example
async function robustBlobOperation() {
  try {
    return await put('test.txt', 'content', { access: 'public' });
  } catch (error) {
    const errorInfo = analyzeBlobError(error);
    
    console.error(`${errorInfo.type}: ${errorInfo.message}`);
    console.log(`Suggested action: ${errorInfo.suggestedAction}`);
    
    if (errorInfo.isRetryable) {
      console.log('This error is retryable');
      // Implement retry logic
    }
    
    throw error;
  }
}

Retry with Exponential Backoff

import { 
  BlobError, 
  BlobServiceNotAvailable, 
  BlobServiceRateLimited 
} from '@vercel/blob';

async function withRetry<T>(
  operation: () => Promise<T>,
  maxRetries = 3,
  baseDelay = 1000
): Promise<T> {
  for (let attempt = 1; attempt <= maxRetries; attempt++) {
    try {
      return await operation();
    } catch (error) {
      const isRetryable = 
        error instanceof BlobServiceNotAvailable ||
        error instanceof BlobServiceRateLimited;
        
      if (!isRetryable || attempt === maxRetries) {
        throw error;
      }
      
      const delay = baseDelay * Math.pow(2, attempt - 1);
      console.log(`Attempt ${attempt} failed, retrying in ${delay}ms...`);
      await new Promise(resolve => setTimeout(resolve, delay));
    }
  }
  
  throw new Error('Max retries exceeded');
}

// Usage
const result = await withRetry(async () => {
  return put('test.txt', 'content', { access: 'public' });
});

Error Boundaries for React

import React from 'react';
import { BlobError } from '@vercel/blob';

interface Props {
  children: React.ReactNode;
}

interface State {
  hasError: boolean;
  error?: Error;
}

class BlobErrorBoundary extends React.Component<Props, State> {
  constructor(props: Props) {
    super(props);
    this.state = { hasError: false };
  }

  static getDerivedStateFromError(error: Error): State {
    return { hasError: true, error };
  }

  componentDidCatch(error: Error) {
    if (error instanceof BlobError) {
      console.error('Blob operation failed:', error.message);
      // Log to error reporting service
    }
  }

  render() {
    if (this.state.hasError) {
      return (
        <div className="error-boundary">
          <h2>Upload Failed</h2>
          <p>
            {this.state.error instanceof BlobError
              ? 'There was an issue with the file upload. Please try again.'
              : 'An unexpected error occurred.'}
          </p>
          <button onClick={() => this.setState({ hasError: false })}>
            Try Again
          </button>
        </div>
      );
    }

    return this.props.children;
  }
}

Constants

Maximum Limits

const MAXIMUM_PATHNAME_LENGTH = 950;  // Maximum allowed pathname length

Best Practices

Error Logging

import { BlobError } from '@vercel/blob';

function logBlobError(error: unknown, operation: string, context?: any) {
  if (error instanceof BlobError) {
    console.error('Blob Error:', {
      operation,
      type: error.constructor.name,
      message: error.message,
      context,
      timestamp: new Date().toISOString(),
    });
  } else {
    console.error('Non-Blob Error:', {
      operation,
      error: String(error),
      context,
      timestamp: new Date().toISOString(),
    });
  }
}

Graceful Degradation

import { put, BlobError } from '@vercel/blob';

async function uploadWithFallback(pathname: string, content: any) {
  try {
    return await put(pathname, content, { access: 'public' });
  } catch (error) {
    if (error instanceof BlobError) {
      console.warn('Blob upload failed, falling back to local storage');
      // Implement fallback logic
      return { url: `local://fallback/${pathname}` };
    }
    throw error;
  }
}

Install with Tessl CLI

npx tessl i tessl/npm-vercel--blob

docs

client-api.md

errors.md

index.md

multipart-uploads.md

server-api.md

tile.json