|
2 | 2 |
|
3 | 3 | from contextlib import asynccontextmanager |
4 | 4 |
|
5 | | -from fastapi import FastAPI, HTTPException |
| 5 | +from fastapi import FastAPI, HTTPException, Request |
6 | 6 | from fastapi.exception_handlers import http_exception_handler |
7 | 7 | from fastapi.routing import APIRouter |
8 | 8 | from loguru import logger |
|
21 | 21 | ) |
22 | 22 | from basic_memory.api.v2.routers.project_router import list_projects |
23 | 23 | from basic_memory.config import init_api_logging |
| 24 | +from basic_memory.services.exceptions import EntityAlreadyExistsError |
24 | 25 | from basic_memory.services.initialization import initialize_app |
25 | 26 |
|
26 | 27 |
|
@@ -93,6 +94,31 @@ async def lifespan(app: FastAPI): # pragma: no cover |
93 | 94 | # V2 routers are the only public API surface |
94 | 95 |
|
95 | 96 |
|
| 97 | +@app.exception_handler(EntityAlreadyExistsError) |
| 98 | +async def entity_already_exists_error_handler( |
| 99 | + request: Request, exc: EntityAlreadyExistsError |
| 100 | +): |
| 101 | + """Handle entity creation conflicts (e.g., file already exists). |
| 102 | +
|
| 103 | + This is expected behavior when users try to create notes that exist, |
| 104 | + so log at INFO level instead of ERROR. |
| 105 | + """ |
| 106 | + logger.info( |
| 107 | + "Entity already exists", |
| 108 | + url=str(request.url), |
| 109 | + method=request.method, |
| 110 | + path=request.url.path, |
| 111 | + error=str(exc), |
| 112 | + ) |
| 113 | + return await http_exception_handler( |
| 114 | + request, |
| 115 | + HTTPException( |
| 116 | + status_code=409, |
| 117 | + detail="Note already exists. Use edit_note to modify it, or delete it first.", |
| 118 | + ), |
| 119 | + ) |
| 120 | + |
| 121 | + |
96 | 122 | @app.exception_handler(Exception) |
97 | 123 | async def exception_handler(request, exc): # pragma: no cover |
98 | 124 | logger.exception( |
|
0 commit comments