Files
twenty/packages/twenty-server
Kartik Pant eefae87296 fix: surface proper errors for People create/delete constraint violations (#21270)
## Summary

Fixes #21119 — `createPerson` / `deletePerson` mutations fail with a
generic `INTERNAL_SERVER_ERROR: "Data validation error."` instead of a
meaningful error, blocking core CRM record management.

## Root Cause

The `computeTwentyORMException` function in `twenty-orm` contains a
catch-all block that matches **every known Postgres error code** via
`Object.values(POSTGRESQL_ERROR_CODES).includes(errorCode)` and discards
all error detail, throwing:

```ts
throw new PostgresException('Data validation error.', errorCode);
```

This `PostgresException` is then converted by the GraphQL error handler
into `INTERNAL_SERVER_ERROR`, masking the real constraint violation.
Common mutations like `createPerson` that hit:
- **`NOT_NULL_VIOLATION` (23502)** — a required field is missing
- **`FOREIGN_KEY_VIOLATION` (23503)** — referenced record missing or
deletion blocked by a FK
- **`RESTRICT_VIOLATION` (23001)** — record deletion blocked by a
referencing row

...all silently surface as the same opaque `"Data validation error."`
with `INTERNAL_SERVER_ERROR`.

Already handled correctly before the catch-all:
- `UNIQUE_VIOLATION` → delegates to `handleDuplicateKeyError` 
- `INVALID_TEXT_REPRESENTATION` → `TwentyORMException(INVALID_INPUT)` 
- Query read timeout → `TwentyORMException(QUERY_READ_TIMEOUT)` 

## Fix

Add explicit handling **before** the catch-all for the four most common
data-integrity constraint errors, converting them to
`TwentyORMException(INVALID_INPUT)` with a clear user-facing message.
The GraphQL error handler then returns `BAD_USER_INPUT` (400) instead of
`INTERNAL_SERVER_ERROR` (500).

## Changes

###
`packages/twenty-server/src/engine/twenty-orm/error-handling/compute-twenty-orm-exception.ts`

Added specific handling for:
| Postgres Code | Constant | User-facing message |
|---|---|---|
| `23502` | `NOT_NULL_VIOLATION` | "A required field is missing. Please
provide all required values and try again." |
| `23503` | `FOREIGN_KEY_VIOLATION` | "This operation references a
record that does not exist or cannot be modified due to existing
relationships." |
| `23001` | `RESTRICT_VIOLATION` | "This record cannot be deleted
because it is still referenced by other records." |

## Before / After

**Before:**
```json
{
  "data": { "createPerson": null },
  "errors": [{
    "message": "Data validation error.",
    "extensions": { "code": "INTERNAL_SERVER_ERROR" }
  }]
}
```

**After (e.g. NOT_NULL_VIOLATION):**
```json
{
  "data": { "createPerson": null },
  "errors": [{
    "message": "A required field is missing. Please provide all required values and try again.",
    "extensions": { "code": "BAD_USER_INPUT" }
  }]
}
```

---------

Co-authored-by: Pantkartik <pantkartik@github.com>
Co-authored-by: Charles Bochet <charles@twenty.com>
Co-authored-by: Etienne <45695613+etiennejouan@users.noreply.github.com>
2026-06-22 11:45:25 +02:00
..
2026-04-02 11:20:08 +00:00
2026-04-02 11:20:08 +00:00