Compare commits

...
Author SHA1 Message Date
Sonarly Claude Code ff7aed1971 DELETE person fails with misleading "Data validation error" masking a PostgreSQL deadlock
https://sonarly.com/issue/3840?type=bug

A PostgreSQL deadlock during a DELETE on the person table is caught by an overly broad error handler that replaces all PostgreSQL error messages with "Data validation error.", returning a misleading HTTP 500 to the API consumer.

Fix: The catch-all in `computeTwentyORMException` replaced every unhandled PostgreSQL error message with the hardcoded string `"Data validation error."`, including transient concurrency errors like deadlocks (`40P01`) and serialization failures (`40001`). This made it impossible for API consumers to distinguish a permanent validation failure from a transient error that would succeed on retry.

The fix adds an explicit guard **before** the catch-all that intercepts `DEADLOCK_DETECTED` and `SERIALIZATION_FAILURE` and returns a `TwentyORMException` (instead of a `PostgresException`) with:
- The real `error.message` (e.g., `"deadlock detected"`) — preserving debuggability
- `TwentyORMExceptionCode.QUERY_READ_TIMEOUT` — the closest existing code for "transient, please retry"
- A user-friendly message: `"We are experiencing a temporary issue with our database. Please try again later."` — consistent with the existing `QUERY_READ_TIMEOUT` pattern

Using `TwentyORMException` also means the error is caught by `workspaceQueryRunnerRestApiExceptionHandler` rather than falling through to `HttpExceptionHandlerService`, which was the path that produced the misleading `InternalServerErrorException: Data validation error.`.

```typescript file=packages/twenty-server/src/engine/twenty-orm/error-handling/compute-twenty-orm-exception.ts lines=61-72
    if (
      errorCode === POSTGRESQL_ERROR_CODES.DEADLOCK_DETECTED ||
      errorCode === POSTGRESQL_ERROR_CODES.SERIALIZATION_FAILURE
    ) {
      return new TwentyORMException(
        error.message,
        TwentyORMExceptionCode.QUERY_READ_TIMEOUT,
        {
          userFriendlyMessage: msg`We are experiencing a temporary issue with our database. Please try again later.`,
        },
      );
    }
```
2026-03-04 10:14:47 +00:00
@@ -58,6 +58,19 @@ export const computeTwentyORMException = async (
);
}
if (
errorCode === POSTGRESQL_ERROR_CODES.DEADLOCK_DETECTED ||
errorCode === POSTGRESQL_ERROR_CODES.SERIALIZATION_FAILURE
) {
return new TwentyORMException(
error.message,
TwentyORMExceptionCode.QUERY_READ_TIMEOUT,
{
userFriendlyMessage: msg`We are experiencing a temporary issue with our database. Please try again later.`,
},
);
}
if (
isDefined(errorCode) &&
Object.values(POSTGRESQL_ERROR_CODES).includes(errorCode)