|
37 | 37 | AgentProfileStore, |
38 | 38 | OpenHandsAgentProfile, |
39 | 39 | ProfileLimitExceeded, |
40 | | - ProfileReferenced, |
41 | 40 | ProfileVerificationSettings, |
42 | 41 | validate_agent_profile, |
43 | 42 | ) |
@@ -107,16 +106,14 @@ class RenameAgentProfileRequest(BaseModel): |
107 | 106 |
|
108 | 107 | @contextmanager |
109 | 108 | def _store_errors() -> Iterator[None]: |
110 | | - """Map ``AgentProfileStore`` / FK errors to HTTP responses.""" |
| 109 | + """Map ``AgentProfileStore`` errors to HTTP responses. |
| 110 | +
|
| 111 | + Mirrors ``profiles_router._store_errors``: ``TimeoutError`` and |
| 112 | + ``ValueError`` only. ``FileNotFoundError`` / ``FileExistsError`` are handled |
| 113 | + inline per-endpoint so each gets a clean, resource-specific message. |
| 114 | + """ |
111 | 115 | try: |
112 | 116 | yield |
113 | | - except ProfileReferenced as e: |
114 | | - # Names the referrers so the caller knows what to detach first. |
115 | | - raise HTTPException(status_code=status.HTTP_409_CONFLICT, detail=str(e)) |
116 | | - except FileExistsError as e: |
117 | | - raise HTTPException(status_code=status.HTTP_409_CONFLICT, detail=str(e)) |
118 | | - except FileNotFoundError as e: |
119 | | - raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=str(e)) |
120 | 117 | except TimeoutError: |
121 | 118 | raise HTTPException( |
122 | 119 | status_code=status.HTTP_503_SERVICE_UNAVAILABLE, |
@@ -348,8 +345,14 @@ async def get_agent_profile( |
348 | 345 | cipher = get_cipher(request) |
349 | 346 |
|
350 | 347 | store = AgentProfileStore() |
351 | | - with _store_errors(): |
352 | | - profile = store.load(name, cipher=cipher) |
| 348 | + try: |
| 349 | + with _store_errors(): |
| 350 | + profile = store.load(name, cipher=cipher) |
| 351 | + except FileNotFoundError: |
| 352 | + raise HTTPException( |
| 353 | + status_code=status.HTTP_404_NOT_FOUND, |
| 354 | + detail=f"Agent profile '{name}' not found", |
| 355 | + ) |
353 | 356 |
|
354 | 357 | # The store leaves skills[].mcp_tools encrypted on load; decrypt to plaintext |
355 | 358 | # so the expose serializer can correctly redact / re-encrypt / reveal them. |
@@ -381,17 +384,12 @@ async def save_agent_profile( |
381 | 384 | try: |
382 | 385 | profile = validate_agent_profile({**body, "name": name}) |
383 | 386 | except ValidationError as e: |
384 | | - # Surface field locations + error types so the client can fix the body, |
385 | | - # but omit ``input``/``msg`` — a nested mcp_tools MCPConfig error embeds |
386 | | - # the input (which may carry secrets) in its message. |
| 387 | + # Match FastAPI's request-validation shape (``detail`` is a list of error |
| 388 | + # objects), but surface only ``loc``/``type`` — a nested mcp_tools |
| 389 | + # MCPConfig error embeds the input (which may carry secrets) in ``msg``. |
387 | 390 | raise HTTPException( |
388 | 391 | status_code=status.HTTP_422_UNPROCESSABLE_ENTITY, |
389 | | - detail={ |
390 | | - "message": "Invalid agent profile", |
391 | | - "errors": [ |
392 | | - {"loc": err["loc"], "type": err["type"]} for err in e.errors() |
393 | | - ], |
394 | | - }, |
| 392 | + detail=[{"loc": err["loc"], "type": err["type"]} for err in e.errors()], |
395 | 393 | ) |
396 | 394 | except Exception: |
397 | 395 | # Any other validation failure (e.g. SkillValidationError from a |
@@ -485,8 +483,19 @@ async def rename_agent_profile( |
485 | 483 | ``new_name`` is taken. |
486 | 484 | """ |
487 | 485 | store = AgentProfileStore() |
488 | | - with _store_errors(): |
489 | | - store.rename(name, body.new_name) |
| 486 | + try: |
| 487 | + with _store_errors(): |
| 488 | + store.rename(name, body.new_name) |
| 489 | + except FileNotFoundError: |
| 490 | + raise HTTPException( |
| 491 | + status_code=status.HTTP_404_NOT_FOUND, |
| 492 | + detail=f"Agent profile '{name}' not found", |
| 493 | + ) |
| 494 | + except FileExistsError: |
| 495 | + raise HTTPException( |
| 496 | + status_code=status.HTTP_409_CONFLICT, |
| 497 | + detail=f"Agent profile '{body.new_name}' already exists", |
| 498 | + ) |
490 | 499 |
|
491 | 500 | if name == body.new_name: |
492 | 501 | message = f"Agent profile '{name}' unchanged (same name)" |
|
0 commit comments