Skip to content

Error Handling

filestore uses a structured exception hierarchy and never crashes your endpoint with unhandled exceptions.

Design Philosophy

  • Configuration errors raise immediately at startup — missing fields, duplicate names, invalid size bounds, or an engine class where an instance is expected all fail fast, before any request is served
  • Validation failures are captured per-file in FileData.error — they don't raise exceptions
  • Backend failures are caught and wrapped into failed FileData results
  • Unexpected errors are logged and returned as a failed Store

Exception Hierarchy

graph TD
    A["FileStoreError"] --> B["ConfigurationError"]
    A --> C["ValidationError"]
    A --> D["StorageError"]
    A --> E["MissingDependencyError"]
    A --> F["NotSupportedError"]

All exceptions inherit from FileStoreError:

from filestore import (
    FileStoreError,          # Base
    ConfigurationError,      # Bad config / bad engine setup
    ValidationError,         # File rejected by a size check
    StorageError,            # Backend failed to persist/delete/presign
    MissingDependencyError,  # Cloud SDK extra not installed
    NotSupportedError,       # Operation not supported by this engine
)

Graceful Failure Pattern

from fastapi import Depends, FastAPI, HTTPException
from filestore import FileStore, LocalEngine, Store

storage = FileStore("file", required=True, engine=LocalEngine(base_dir="uploads"))

@app.post("/upload")
async def upload(store: Store = Depends(storage)):
    if not store:  # status is not "completed"
        raise HTTPException(
            status_code=422,
            detail={"status": store.status, "errors": store.errors, "message": store.message},
        )
    file_data = store.first("file")
    return {"url": file_data.url}

Fail-Fast Configuration

Misconfiguration surfaces when the module is imported, not on the first request:

FileStore()                                   # ConfigurationError: no fields
FileStore(fields=[FileField(name="a"),
                  FileField(name="a")])        # ConfigurationError: duplicates
FileStore("f", engine=MemoryEngine)            # ConfigurationError: class, not instance
StoreConfig(min_file_size=10, max_file_size=1) # pydantic ValidationError

Engine Operations

Direct engine calls (delete, presign_upload, presign_download) raise rather than returning failed results — handle them where you call them:

from filestore import NotSupportedError, StorageError

try:
    url = await engine.presign_upload("uploads/a.png")
except NotSupportedError:
    ...  # local/memory engine — fall back to proxied upload
except StorageError as err:
    ...  # signing failed

Catching All Filestore Errors

try:
    storage = FileStore("", engine=LocalEngine())
except FileStoreError as err:
    print(f"Setup failed: {err}")

Note

Validation failures during upload do not raise exceptions. They are captured in FileData.error and Store.errors.