REQ-C-028: ALREADY_EXISTS Response Pattern
Tier: Command Contract | Priority: P1
Source: Silent assumption — agents retry failed creates; if create fails because the resource already exists, the agent needs the existing resource returned so it can proceed without a separate get call
Addresses: Severity: High / Token Spend: Medium / Time: Medium / Context: Low
Description
Commands that create resources MUST handle the "resource already exists" case by returning a structured ALREADY_EXISTS response — not a generic error. The response MUST include the existing resource in the data field (identical to what a get command would return), set "ok": false, exit 6 (CONFLICT) with error.code: "ALREADY_EXISTS", and set "retryable": false. The command's CONFLICT (6) entry SHOULD list ALREADY_EXISTS in its error_codes, so an agent reading the manifest knows before the first call that exit 6 means the resource already exists.
This pattern allows agents that retry failed creates to recover gracefully: they receive the existing resource and can proceed as if the create succeeded. Without this pattern, agents must make a separate get call after every create failure to determine whether the failure was a conflict or a genuine error.
The inverse — delete on a non-existent resource — MUST exit 0 with {"ok": true, "data": {"status": "not_found"}}.
Acceptance Criteria
tool resource create --name foocalled twice returns the resource on both calls- Second call:
"ok": false, exit6(CONFLICT),error.code: "ALREADY_EXISTS",datacontains the existing resource - Agent can use
datafrom the second call directly without a follow-upget - Delete of non-existent resource exits
0with structurednot_foundconfirmation CONFLICT (6)is declared in the command's exit code table with a description naming the already-exists case- The
CONFLICT (6)entry lists"ALREADY_EXISTS"inerror_codes, together with any othererror.codethe command emits under exit6
Schema
exit-code-entry.md — commands declare CONFLICT (6) for the already-exists case, with ALREADY_EXISTS in error_codes · response-envelope.md — data carries the existing resource as a declared failure payload
Wire Format
tool resource create --schema → .exit_codes:
{
"exit_codes": {
"0": { "name": "SUCCESS", "description": "The resource is created", "retryable": false, "side_effects": "complete" },
"6": { "name": "CONFLICT", "description": "A resource with this name already exists; data holds it", "retryable": false, "side_effects": "none", "error_codes": ["ALREADY_EXISTS"] }
}
}
Create called on existing resource:
{
"ok": false,
"data": {
"id": "res_123",
"name": "foo",
"created_at": "2026-01-01T00:00:00Z",
"status": "active"
},
"error": {
"code": "ALREADY_EXISTS",
"message": "Resource 'foo' already exists",
"retryable": false,
"conflict_id": "res_123"
},
"warnings": [],
"meta": { "exit_code": 6, "duration_ms": 41 }
}
Delete called on non-existent resource:
{
"ok": true,
"data": {"status": "not_found", "id": "res_999"},
"error": null,
"warnings": [],
"meta": { "exit_code": 0, "duration_ms": 37 }
}
Example
Agent create-or-get pattern:
result = run("tool resource create --name foo")
if result.exit_code == 0:
resource = result.data # newly created
elif result.error.code == "ALREADY_EXISTS":
resource = result.data # existing — no extra get call needed
else:
raise result.error # genuine failure
Related
| Requirement | Tier | Relationship |
|---|---|---|
| REQ-C-007 | C | Composes: idempotency key prevents ALREADY_EXISTS on intentional retries |
| REQ-C-003 | C | Provides: effect field distinguishes create (non-idempotent) from apply (idempotent) |
| REQ-C-001 | C | Provides: ALREADY_EXISTS exit code must be declared in the command's exit code table |
| REQ-F-004 | F | Provides: response envelope that carries the existing resource in data on conflict |