The Vercel Blob JavaScript API client for cloud blob storage operations
—
Comprehensive error system with specific error types for different failure scenarios and detailed error messages.
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.
All blob-related errors inherit from the base BlobError class, which extends the standard JavaScript Error.
class BlobError extends Error {
constructor(message: string);
}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:
BLOB_READ_WRITE_TOKENExample:
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
}
}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 blobdel() on non-existent blobcopy() from non-existent sourceExample:
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
}
}Thrown when the blob store itself cannot be found.
/**
* Thrown when the blob store cannot be found
*/
class BlobStoreNotFoundError extends BlobError {
constructor();
}Common Causes:
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:
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:
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
}
}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:
MAXIMUM_PATHNAME_LENGTH)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:
Thrown when the blob service is temporarily unavailable.
/**
* Thrown when the blob service is temporarily unavailable
*/
class BlobServiceNotAvailable extends BlobError {
constructor();
}Common Causes:
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;
}
}
}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;
}
}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');
}
}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;
}
}Thrown for unexpected or unhandled error conditions.
/**
* Thrown for unknown or unexpected error conditions
*/
class BlobUnknownError extends BlobError {
constructor();
}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
}
}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;
}
}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' });
});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;
}
}const MAXIMUM_PATHNAME_LENGTH = 950; // Maximum allowed pathname lengthimport { 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(),
});
}
}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