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
FileDataresults - 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.