Error Handling

Last updated:

Error Handling

Visulima storage provides consistent error handling across all storage backends with standardized error codes and messages.

Error Types

Upload Errors

import { ERRORS, isUploadError, throwErrorCode } from "@visulima/storage";

try {
    await storage.get({ id: "nonexistent" });
} catch (error) {
    if (isUploadError(error)) {
        console.log("Error code:", error.UploadErrorCode);
        console.log("Status:", error.statusCode);
        console.log("Message:", error.message);
    }
}

Standard Error Codes

enum ERRORS {
    BAD_REQUEST = "BadRequest",
    CHECKSUM_MISMATCH = "ChecksumMismatch",
    FILE_CONFLICT = "FileConflict",
    FILE_ERROR = "FileError",
    FILE_LOCKED = "FileLocked",
    FILE_NOT_ALLOWED = "FileNotAllowed",
    FILE_NOT_FOUND = "FileNotFound",
    FORBIDDEN = "Forbidden",
    GONE = "Gone",
    INVALID_FILE_NAME = "InvalidFileName",
    INVALID_FILE_SIZE = "InvalidFileSize",
    INVALID_RANGE = "InvalidRange",
    INVALID_TYPE = "Invalidtype",
    METHOD_NOT_ALLOWED = "MethodNotAllowed",
    REQUEST_ABORTED = "RequestAborted",
    REQUEST_ENTITY_TOO_LARGE = "RequestEntityTooLarge",
    STORAGE_BUSY = "StorageBusy",
    STORAGE_ERROR = "StorageError",
    TOO_MANY_REQUESTS = "TooManyRequests",
    UNKNOWN_ERROR = "UnknownError",
    UNPROCESSABLE_ENTITY = "UnprocessableEntity",
    UNSUPPORTED_CHECKSUM_ALGORITHM = "UnsupportedChecksumAlgorithm",
    UNSUPPORTED_MEDIA_TYPE = "UnsupportedMediaType",
}

Error Response Format

All errors follow a consistent format:

interface HttpError {
    code: string; // Error code (e.g., "FileNotFound")
    message: string; // Human-readable message
    statusCode: number; // HTTP status code
    name?: string; // Error name
    body?: any; // Additional error details
}

Handling Common Errors

File Not Found

try {
    await storage.get({ id: "nonexistent" });
} catch (error) {
    if (isUploadError(error) && error.UploadErrorCode === ERRORS.FILE_NOT_FOUND) {
        return res.status(404).json({ error: "File not found" });
    }
    throw error;
}

File Too Large

try {
    await storage.create({
        contentType: "image/jpeg",
        size: 100 * 1024 * 1024, // 100MB
    });
} catch (error) {
    if (isUploadError(error) && error.UploadErrorCode === ERRORS.REQUEST_ENTITY_TOO_LARGE) {
        return res.status(413).json({ error: "File too large" });
    }
    throw error;
}

Unsupported Media Type

try {
    await storage.create({
        contentType: "application/x-executable",
        size: 1024,
    });
} catch (error) {
    if (isUploadError(error) && error.UploadErrorCode === ERRORS.UNSUPPORTED_MEDIA_TYPE) {
        return res.status(415).json({ error: "Unsupported file type" });
    }
    throw error;
}

Checksum Mismatch

try {
    await storage.write({
        id: "file-id",
        body: stream,
        checksum: "invalid-checksum",
        checksumAlgorithm: "md5",
    });
} catch (error) {
    if (isUploadError(error) && error.UploadErrorCode === ERRORS.CHECKSUM_MISMATCH) {
        return res.status(460).json({ error: "Checksum mismatch" });
    }
    throw error;
}

Custom Error Handling

Custom Error Responses

const storage = new DiskStorage({
    directory: "./uploads",
    onError: (error: HttpError) => {
        // Customize error response
        return {
            body: {
                error: {
                    code: error.code,
                    message: error.message,
                    timestamp: new Date().toISOString(),
                },
            },
            headers: {
                "X-Error-Code": error.code,
            },
            statusCode: error.statusCode,
        };
    },
});

Error Logging

const storage = new DiskStorage({
    directory: "./uploads",
    logger: {
        error: (message, ...args) => {
            console.error(`[Storage Error] ${message}`, ...args);
            // Send to error tracking service
            errorTracker.captureException(new Error(message));
        },
        debug: (message, ...args) => {
            console.debug(`[Storage Debug] ${message}`, ...args);
        },
        info: (message, ...args) => {
            console.info(`[Storage Info] ${message}`, ...args);
        },
    },
});

Storage-Specific Errors

AWS S3 Errors

try {
    await s3Storage.get({ id: "file-id" });
} catch (error) {
    // AWS SDK errors are normalized
    if (error.$metadata) {
        console.log("AWS Status:", error.$metadata.httpStatusCode);
        console.log("AWS Code:", error.Code);
    }
}

Azure Storage Errors

try {
    await azureStorage.get({ id: "file-id" });
} catch (error) {
    // Azure errors include statusCode
    if (error.statusCode) {
        console.log("Azure Status:", error.statusCode);
    }
}

Error Middleware

Express Error Middleware

app.use((error: Error, req: express.Request, res: express.Response, next: express.NextFunction) => {
    if (isUploadError(error)) {
        const statusCode = error.statusCode || 500;
        return res.status(statusCode).json({
            error: {
                code: error.UploadErrorCode,
                message: error.message,
            },
        });
    }

    // Handle other errors
    res.status(500).json({ error: "Internal server error" });
});

Hono Error Handler

app.onError((error, c) => {
    if (isUploadError(error)) {
        return c.json(
            {
                error: {
                    code: error.UploadErrorCode,
                    message: error.message,
                },
            },
            error.statusCode || 500,
        );
    }

    return c.json({ error: "Internal server error" }, 500);
});

Best Practices

  1. Always check error types - Use isUploadError() to identify upload errors
  2. Handle specific error codes - Provide user-friendly messages for common errors
  3. Log errors appropriately - Include context for debugging
  4. Return appropriate status codes - Use standard HTTP status codes
  5. Don't expose internal details - Sanitize error messages for production
Support

Contribute to our work and keep us going

Community is the heart of open source. The success of our packages wouldn't be possible without the incredible contributions of users, testers, and developers who collaborate with us every day.Want to get involved? Here are some tips on how you can make a meaningful impact on our open source projects.

Ready to help us out?

Be sure to check out the package's contribution guidelines first. They'll walk you through the process on how to properly submit an issue or pull request to our repositories.

Submit a pull request

Found something to improve? Fork the repo, make your changes, and open a PR. We review every contribution and provide feedback to help you get merged.

Good first issues

Simple issues suited for people new to open source development, and often a good place to start working on a package.
View good first issues