Skip to content

Commit eda4828

Browse files
Simon Rosenbergclaude
andcommitted
Address review 3: 500 on settings corruption, UUID validation, comment trim
- activate: a corrupted/mis-keyed settings file (RuntimeError) is a server-side integrity failure → 500, not 409. Test added. - SettingsUpdateRequest.active_agent_profile_id gains a UUID format pattern so malformed pointers are rejected at the HTTP layer (mirrors active_profile). - Drop a diff-narrative comment in PersistedSettings.update(). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent a48296e commit eda4828

5 files changed

Lines changed: 41 additions & 7 deletions

File tree

openhands-agent-server/openhands/agent_server/agent_profiles_router.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -512,10 +512,12 @@ def set_pointer(settings: PersistedSettings) -> PersistedSettings:
512512
logger.error("Failed to activate agent profile - file I/O error")
513513
raise HTTPException(status_code=500, detail="Failed to activate agent profile")
514514
except RuntimeError as e:
515+
# A corrupted / mis-keyed settings file is a server-side integrity
516+
# failure, not a client conflict.
515517
logger.error(f"Failed to activate agent profile: {e}")
516518
raise HTTPException(
517-
status_code=status.HTTP_409_CONFLICT,
518-
detail="Settings file is corrupted or encrypted with a different key",
519+
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
520+
detail="Failed to activate agent profile",
519521
)
520522

521523
logger.info(f"Activated agent profile id '{profile_id}'")

openhands-agent-server/openhands/agent_server/persistence/models.py

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -254,11 +254,9 @@ def update(self, payload: SettingsUpdatePayload) -> None:
254254
if new_misc is not None:
255255
self.misc_settings = new_misc
256256

257-
# Update active_profile if explicitly provided (including None to clear)
257+
# Update pointers if explicitly provided (including None to clear)
258258
if "active_profile" in payload:
259259
self.active_profile = payload["active_profile"]
260-
261-
# Same for the AgentProfile pointer (including None to clear)
262260
if "active_agent_profile_id" in payload:
263261
self.active_agent_profile_id = payload["active_agent_profile_id"]
264262
finally:

openhands-sdk/openhands/sdk/settings/api_models.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,14 @@
3333
from openhands.sdk.llm.llm_profile_store import PROFILE_NAME_PATTERN
3434

3535

36+
# An AgentProfile's stable id is a UUID (the pointer target); reject malformed
37+
# values at the HTTP layer, mirroring ``active_profile``'s name pattern.
38+
UUID_PATTERN = (
39+
r"^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-"
40+
r"[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$"
41+
)
42+
43+
3644
if TYPE_CHECKING:
3745
from .model import AgentSettingsConfig, ConversationSettings
3846

@@ -119,6 +127,7 @@ class SettingsUpdateRequest(BaseModel):
119127
)
120128
active_agent_profile_id: str | None = Field(
121129
default=None,
130+
pattern=UUID_PATTERN,
122131
description="Stable id of the active AgentProfile to persist; null clears it.",
123132
)
124133

tests/agent_server/test_agent_profiles_router.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -375,6 +375,21 @@ def test_activate_unknown_id_returns_404(client, store):
375375
assert response.status_code == 404
376376

377377

378+
def test_activate_settings_corruption_returns_500(client, store, monkeypatch):
379+
"""A corrupted/mis-keyed settings file is a server-side failure (500)."""
380+
from openhands.agent_server.persistence.store import FileSettingsStore
381+
382+
store.save(OpenHandsAgentProfile(name="p", llm_profile_ref="x"))
383+
profile_id = client.get("/api/agent-profiles/p").json()["profile"]["id"]
384+
385+
def boom(self, *args, **kwargs):
386+
raise RuntimeError("settings file corrupted")
387+
388+
monkeypatch.setattr(FileSettingsStore, "update", boom)
389+
response = client.post(f"/api/agent-profiles/{profile_id}/activate")
390+
assert response.status_code == 500
391+
392+
378393
# ── Seed fidelity (migration preserves the user's launch config) ────────────
379394

380395

tests/agent_server/test_settings_router.py

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -524,14 +524,15 @@ def test_patch_settings_rejects_invalid_active_profile(client_with_settings):
524524

525525
def test_patch_settings_active_agent_profile_id_independent(client_with_settings):
526526
"""active_agent_profile_id sets/clears independently of active_profile."""
527+
agent_id = "12345678-1234-1234-1234-1234567890ab"
527528
set_response = client_with_settings.patch(
528529
"/api/settings",
529-
json={"active_profile": "fast-profile", "active_agent_profile_id": "abc-123"},
530+
json={"active_profile": "fast-profile", "active_agent_profile_id": agent_id},
530531
)
531532
assert set_response.status_code == 200
532533
body = set_response.json()
533534
assert body["active_profile"] == "fast-profile"
534-
assert body["active_agent_profile_id"] == "abc-123"
535+
assert body["active_agent_profile_id"] == agent_id
535536

536537
# Clearing the agent pointer must leave the LLM profile pointer untouched.
537538
clear_response = client_with_settings.patch(
@@ -548,6 +549,15 @@ def test_patch_settings_active_agent_profile_id_independent(client_with_settings
548549
assert refetch["active_profile"] == "fast-profile"
549550

550551

552+
def test_patch_settings_rejects_malformed_active_agent_profile_id(client_with_settings):
553+
"""A non-UUID active_agent_profile_id is rejected at the HTTP layer."""
554+
response = client_with_settings.patch(
555+
"/api/settings",
556+
json={"active_agent_profile_id": "not-a-uuid"},
557+
)
558+
assert response.status_code == 422
559+
560+
551561
def test_existing_settings_load_with_null_active_agent_profile_id(
552562
temp_persistence_dir, config_with_settings
553563
):

0 commit comments

Comments
 (0)