CB-599: a capacity refusal must tell the caller why #93

Merged
ltms merged 1 commits from worker/cb599-740fe4-16 into main 2026-08-16 17:54:45 +02:00
Member

PlacementException extends IllegalStateException, which neither BridgedApp.spawnMember nor BridgeMcp.spawn caught, so a maxLoad/quarantine/all-exhausted refusal fell through to a blank text/plain 500 on REST and lost its message on MCP.

Fix: catch PlacementException on both surfaces (before the unrelated unknown_profile IllegalArgumentException mapping) and return its message structured:

  • REST: {"error":"no_capacity","detail":} with status 503 (valid request, likely to succeed later; distinct from 400 unknown_profile and 403 subscription_boundary).
  • MCP: isError result with text "no capacity: ".

No throw-site messages were reworded. All PlacementException throw sites (FixedPlacementPolicy, WeightedRoundRobinPolicy, PlacementPolicyUtil, CompositePeerLauncher.enforceMaxLoad/enforceNotQuarantined) already produce clear messages; the gap was purely in delivery to the caller.

Tests: BridgedAppTest#spawnAtMaxLoadIs503WithTheCapacityReasonNotABare500 and BridgeMcpTest#spawnAtMaxLoadSurfacesTheCapacityReason both wire a CompositePeerLauncher with a maxLoad:0 profile and assert the caller can read the reason on each surface.

mvn -f bridged/pom.xml clean install: Tests run: 824, Failures: 0, Errors: 0, Skipped: 0 -- BUILD SUCCESS (run unpiped).

PlacementException extends IllegalStateException, which neither BridgedApp.spawnMember nor BridgeMcp.spawn caught, so a maxLoad/quarantine/all-exhausted refusal fell through to a blank text/plain 500 on REST and lost its message on MCP. Fix: catch PlacementException on both surfaces (before the unrelated unknown_profile IllegalArgumentException mapping) and return its message structured: - REST: {"error":"no_capacity","detail":<message>} with status 503 (valid request, likely to succeed later; distinct from 400 unknown_profile and 403 subscription_boundary). - MCP: isError result with text "no capacity: <message>". No throw-site messages were reworded. All PlacementException throw sites (FixedPlacementPolicy, WeightedRoundRobinPolicy, PlacementPolicyUtil, CompositePeerLauncher.enforceMaxLoad/enforceNotQuarantined) already produce clear messages; the gap was purely in delivery to the caller. Tests: BridgedAppTest#spawnAtMaxLoadIs503WithTheCapacityReasonNotABare500 and BridgeMcpTest#spawnAtMaxLoadSurfacesTheCapacityReason both wire a CompositePeerLauncher with a maxLoad:0 profile and assert the caller can read the reason on each surface. mvn -f bridged/pom.xml clean install: Tests run: 824, Failures: 0, Errors: 0, Skipped: 0 -- BUILD SUCCESS (run unpiped).
agent added 1 commit 2026-08-16 17:43:24 +02:00
CB-599: surface a capacity refusal's reason instead of a bare 500
CI / contract (pull_request) Successful in 50s
CI / build (pull_request) Successful in 1m35s
8a837a2830
PlacementException extends IllegalStateException, which neither BridgedApp
nor BridgeMcp's spawn catch blocks handled, so a maxLoad/quarantine/
all-exhausted refusal fell through to a blank 500 on REST and lost its
message on MCP. Catch it on both surfaces, ahead of the unrelated
IllegalArgumentException(unknown_profile) mapping, and return its message
structured: REST as {"error":"no_capacity","detail":...} with status 503,
MCP as an isError result prefixed "no capacity: ...".
ltms merged commit 28ae27b8e1 into main 2026-08-16 17:54:45 +02:00
Sign in to join this conversation.