Skip to content

Commit 6c08131

Browse files
baozhoutaoclaude
andauthored
docs(protocol): the 409 unique-constraint refusal is UNIQUE_VIOLATION on the wire; DUPLICATE_RECORD is the engine's in-process code (#15362) (#15634)
Claude-Session: https://claude.ai/code/session_012zGPuVVX3deAx9LdjK8jCk Co-authored-by: Claude <noreply@anthropic.com>
1 parent da28b62 commit 6c08131

1 file changed

Lines changed: 14 additions & 14 deletions

File tree

content/docs/protocol/kernel/error-handling.mdx

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -346,27 +346,27 @@ deriving it from this page.
346346
- Check user has permission to see resource (row-level security)
347347
- Resource may have been deleted
348348

349-
#### `DUPLICATE_RECORD`
349+
#### `UNIQUE_VIOLATION`
350350
**HTTP Status:** 409
351-
**Meaning:** Resource with unique constraint already exists
351+
**Meaning:** The write collides with a unique constraint — a record already holds that value
352352

353353
**Example:**
354354
```json
355355
{
356-
"success": false,
357-
"error": {
358-
"code": "DUPLICATE_RECORD",
359-
"message": "Account with email 'john@acme.com' already exists",
360-
"details": {
361-
"resource": "account",
362-
"constraint": "unique",
363-
"field": "email",
364-
"value": "john@acme.com"
365-
}
366-
}
356+
"error": "A record with this email already exists",
357+
"code": "UNIQUE_VIOLATION",
358+
"field": "email",
359+
"object": "account"
367360
}
368361
```
369362

363+
The engine throws `DuplicateRecordError`, whose in-process `code` is
364+
`DUPLICATE_RECORD`; the REST door translates that envelope at the boundary, so
365+
every route answers the wire code `UNIQUE_VIOLATION` and the in-process spelling
366+
never crosses HTTP. The refusal is emitted as the flat body shown above, and
367+
`field` is best-effort — see
368+
[HTTP API](/docs/protocol/kernel/http-protocol) for the degraded shape.
369+
370370
**How to fix:**
371371
- Check for existing resource before creating
372372
- Update existing resource instead of creating new one
@@ -947,7 +947,7 @@ Even error responses can be abused:
947947
// Attacker tries to enumerate user emails
948948
for (let i = 0; i < 1000000; i++) {
949949
await register({ email: `user${i}@example.com` });
950-
// Response: "DUPLICATE_RECORD" or "VALIDATION_ERROR"
950+
// Response: "UNIQUE_VIOLATION" or "VALIDATION_ERROR"
951951
}
952952
```
953953

0 commit comments

Comments
 (0)