From d4303469ff5ff9e85385bb5cb78327ae1a46c6a4 Mon Sep 17 00:00:00 2001 From: Marie <51697796+ijreilly@users.noreply.github.com> Date: Wed, 4 Feb 2026 15:33:30 +0100 Subject: [PATCH] Improve qualification of rest errors (#17629) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Will help close [sentry](https://twenty-v7.sentry.io/issues/6827612895/?environment=prod&environment=prod-eu&project=4507072499810304&query=is%3Aunresolved%20%21issue.type%3A%5Bperformance_consecutive_db_queries%2Cperformance_consecutive_http%2Cperformance_file_io_main_thread%2Cperformance_db_main_thread%2Cperformance_n_plus_one_db_queries%2Cperformance_n_plus_one_api_calls%2Cperformance_p95_endpoint_regression%2Cperformance_slow_db_query%2Cperformance_render_blocking_asset_span%2Cperformance_uncompressed_assets%2Cperformance_http_overhead%2Cperformance_large_http_payload%5D%20timesSeen%3A%3E10&referrer=issue-stream&sort=date): a 400 error (attempt to filter on a `null` id) qualified as a 500 error from rest api. I don't know what was our long term plan on validation for rest api, I suppose it's not a priority, making it acceptable to interceipt some postgres errors based on their messages. Ideally we would have a validation earlier, at args parsing level. before Capture d’écran 2026-02-02 à 14 08
53 after Capture d’écran 2026-02-02 à 14 09
01 --- .../postgres-error-codes.constants.ts | 523 +++++++++--------- ...-runner-rest-api-exception-handler.util.ts | 7 + .../compute-twenty-orm-exception.ts | 19 +- .../messaging-messages-import.cron.command.ts | 4 +- ...-input-validation.integration-spec.ts.snap | 4 +- ...r-input-by-field-metadata-type.constant.ts | 35 +- 6 files changed, 295 insertions(+), 297 deletions(-) diff --git a/packages/twenty-server/src/engine/api/graphql/workspace-query-runner/constants/postgres-error-codes.constants.ts b/packages/twenty-server/src/engine/api/graphql/workspace-query-runner/constants/postgres-error-codes.constants.ts index af0e2fc0ef..bd22bfca5e 100644 --- a/packages/twenty-server/src/engine/api/graphql/workspace-query-runner/constants/postgres-error-codes.constants.ts +++ b/packages/twenty-server/src/engine/api/graphql/workspace-query-runner/constants/postgres-error-codes.constants.ts @@ -1,263 +1,262 @@ // From https://www.postgresql.org/docs/current/errcodes-appendix.html -export const POSTGRESQL_ERROR_CODES = [ - '00000', // successful_completion - '01000', // warning - '0100C', // dynamic_result_sets_returned - '01008', // implicit_zero_bit_padding - '01003', // null_value_eliminated_in_set_function - '01007', // privilege_not_granted - '01006', // privilege_not_revoked - '01004', // string_data_right_truncation - '01P01', // deprecated_feature - '02000', // no_data - '02001', // no_additional_dynamic_result_sets_returned - '03000', // sql_statement_not_yet_complete - '08000', // connection_exception - '08003', // connection_does_not_exist - '08006', // connection_failure - '08001', // sqlclient_unable_to_establish_sqlconnection - '08004', // sqlserver_rejected_establishment_of_sqlconnection - '08007', // transaction_resolution_unknown - '08P01', // protocol_violation - '09000', // triggered_action_exception - '0A000', // feature_not_supported - '0B000', // invalid_transaction_initiation - '0F000', // locator_exception - '0F001', // invalid_locator_specification - '0L000', // invalid_grantor - '0LP01', // invalid_grant_operation - '0P000', // invalid_role_specification - '0Z000', // diagnostics_exception - '0Z002', // stacked_diagnostics_accessed_without_active_handler - '20000', // case_not_found - '21000', // cardinality_violation - '22000', // data_exception - '2202E', // array_subscript_error - '22021', // character_not_in_repertoire - '22008', // datetime_field_overflow - '22012', // division_by_zero - '22005', // error_in_assignment - '2200B', // escape_character_conflict - '22022', // indicator_overflow - '22015', // interval_field_overflow - '2201E', // invalid_argument_for_logarithm - '22014', // invalid_argument_for_ntile_function - '22016', // invalid_argument_for_nth_value_function - '2201F', // invalid_argument_for_power_function - '2201G', // invalid_argument_for_width_bucket_function - '22018', // invalid_character_value_for_cast - '22007', // invalid_datetime_format - '22019', // invalid_escape_character - '2200D', // invalid_escape_octet - '22025', // invalid_escape_sequence - '22P06', // nonstandard_use_of_escape_character - '22010', // invalid_indicator_parameter_value - '22023', // invalid_parameter_value - '22013', // invalid_preceding_or_following_size - '2201B', // invalid_regular_expression - '2201W', // invalid_row_count_in_limit_clause - '2201X', // invalid_row_count_in_result_offset_clause - '2202H', // invalid_tablesample_argument - '2202G', // invalid_tablesample_repeat - '22009', // invalid_time_zone_displacement_value - '2200C', // invalid_use_of_escape_character - '2200G', // most_specific_type_mismatch - '22004', // null_value_not_allowed - '22002', // null_value_no_indicator_parameter - '22003', // numeric_value_out_of_range - '2200H', // sequence_generator_limit_exceeded - '22026', // string_data_length_mismatch - '22001', // string_data_right_truncation - '22011', // substring_error - '22027', // trim_error - '22024', // unterminated_c_string - '2200F', // zero_length_character_string - '22P01', // floating_point_exception - '22P02', // invalid_text_representation - '22P03', // invalid_binary_representation - '22P04', // bad_copy_file_format - '22P05', // untranslatable_character - '2200L', // not_an_xml_document - '2200M', // invalid_xml_document - '2200N', // invalid_xml_content - '2200S', // invalid_xml_comment - '2200T', // invalid_xml_processing_instruction - '22030', // duplicate_json_object_key_value - '22031', // invalid_argument_for_sql_json_datetime_function - '22032', // invalid_json_text - '22033', // invalid_sql_json_subscript - '22034', // more_than_one_sql_json_item - '22035', // no_sql_json_item - '22036', // non_numeric_sql_json_item - '22037', // non_unique_keys_in_a_json_object - '22038', // singleton_sql_json_item_required - '22039', // sql_json_array_not_found - '2203A', // sql_json_member_not_found - '2203B', // sql_json_number_not_found - '2203C', // sql_json_object_not_found - '2203D', // too_many_json_array_elements - '2203E', // too_many_json_object_members - '2203F', // sql_json_scalar_required - '2203G', // sql_json_item_cannot_be_cast_to_target_type - '23000', // integrity_constraint_violation - '23001', // restrict_violation - '23502', // not_null_violation - '23503', // foreign_key_violation - '23505', // unique_violation - '23514', // check_violation - '23P01', // exclusion_violation - '24000', // invalid_cursor_state - '25000', // invalid_transaction_state - '25001', // active_sql_transaction - '25002', // branch_transaction_already_active - '25008', // held_cursor_requires_same_isolation_level - '25003', // inappropriate_access_mode_for_branch_transaction - '25004', // inappropriate_isolation_level_for_branch_transaction - '25005', // no_active_sql_transaction_for_branch_transaction - '25006', // read_only_sql_transaction - '25007', // schema_and_data_statement_mixing_not_supported - '25P01', // no_active_sql_transaction - '25P02', // in_failed_sql_transaction - '25P03', // idle_in_transaction_session_timeout - '25P04', // transaction_timeout - '26000', // invalid_sql_statement_name - '27000', // triggered_data_change_violation - '28000', // invalid_authorization_specification - '28P01', // invalid_password - '2B000', // dependent_privilege_descriptors_still_exist - '2BP01', // dependent_objects_still_exist - '2D000', // invalid_transaction_termination - '2F000', // sql_routine_exception - '2F005', // function_executed_no_return_statement - '2F002', // modifying_sql_data_not_permitted - '2F003', // prohibited_sql_statement_attempted - '2F004', // reading_sql_data_not_permitted - '34000', // invalid_cursor_name - '38000', // external_routine_exception - '38001', // containing_sql_not_permitted - '38002', // modifying_sql_data_not_permitted - '38003', // prohibited_sql_statement_attempted - '38004', // reading_sql_data_not_permitted - '39000', // external_routine_invocation_exception - '39001', // invalid_sqlstate_returned - '39004', // null_value_not_allowed - '39P01', // trigger_protocol_violated - '39P02', // srf_protocol_violated - '39P03', // event_trigger_protocol_violated - '3B000', // savepoint_exception - '3B001', // invalid_savepoint_specification - '3D000', // invalid_catalog_name - '3F000', // invalid_schema_name - '40000', // transaction_rollback - '40002', // transaction_integrity_constraint_violation - '40001', // serialization_failure - '40003', // statement_completion_unknown - '40P01', // deadlock_detected - '42000', // syntax_error_or_access_rule_violation - '42601', // syntax_error - '42501', // insufficient_privilege - '42846', // cannot_coerce - '42803', // grouping_error - '42P20', // windowing_error - '42P19', // invalid_recursion - '42830', // invalid_foreign_key - '42602', // invalid_name - '42622', // name_too_long - '42939', // reserved_name - '42804', // datatype_mismatch - '42P18', // indeterminate_datatype - '42P21', // collation_mismatch - '42P22', // indeterminate_collation - '42809', // wrong_object_type - '428C9', // generated_always - '42703', // undefined_column - '42883', // undefined_function - '42P01', // undefined_table - '42P02', // undefined_parameter - '42704', // undefined_object - '42701', // duplicate_column - '42P03', // duplicate_cursor - '42P04', // duplicate_database - '42723', // duplicate_function - '42P05', // duplicate_prepared_statement - '42P06', // duplicate_schema - '42P07', // duplicate_table - '42712', // duplicate_alias - '42710', // duplicate_object - '42702', // ambiguous_column - '42725', // ambiguous_function - '42P08', // ambiguous_parameter - '42P09', // ambiguous_alias - '42P10', // invalid_column_reference - '42611', // invalid_column_definition - '42P11', // invalid_cursor_definition - '42P12', // invalid_database_definition - '42P13', // invalid_function_definition - '42P14', // invalid_prepared_statement_definition - '42P15', // invalid_schema_definition - '42P16', // invalid_table_definition - '42P17', // invalid_object_definition - '44000', // with_check_option_violation - '53000', // insufficient_resources - '53100', // disk_full - '53200', // out_of_memory - '53300', // too_many_connections - '53400', // configuration_limit_exceeded - '54000', // program_limit_exceeded - '54001', // statement_too_complex - '54011', // too_many_columns - '54023', // too_many_arguments - '55000', // object_not_in_prerequisite_state - '55006', // object_in_use - '55P02', // cant_change_runtime_param - '55P03', // lock_not_available - '55P04', // unsafe_new_enum_value_usage - '57000', // operator_intervention - '57014', // query_canceled - '57P01', // admin_shutdown - '57P02', // crash_shutdown - '57P03', // cannot_connect_now - '57P04', // database_dropped - '57P05', // idle_session_timeout - '58000', // system_error - '58030', // io_error - '58P01', // undefined_file - '58P02', // duplicate_file - 'F0000', // config_file_error - 'F0001', // lock_file_exists - 'HV000', // fdw_error - 'HV005', // fdw_column_name_not_found - 'HV002', // fdw_dynamic_parameter_value_needed - 'HV010', // fdw_function_sequence_error - 'HV021', // fdw_inconsistent_descriptor_information - 'HV024', // fdw_invalid_attribute_value - 'HV007', // fdw_invalid_column_name - 'HV008', // fdw_invalid_column_number - 'HV004', // fdw_invalid_data_type - 'HV006', // fdw_invalid_data_type_descriptors - 'HV091', // fdw_invalid_descriptor_field_identifier - 'HV00B', // fdw_invalid_handle - 'HV00C', // fdw_invalid_option_index - 'HV00D', // fdw_invalid_option_name - 'HV090', // fdw_invalid_string_length_or_buffer_length - 'HV00A', // fdw_invalid_string_format - 'HV009', // fdw_invalid_use_of_null_pointer - 'HV014', // fdw_too_many_handles - 'HV001', // fdw_out_of_memory - 'HV00P', // fdw_no_schemas - 'HV00J', // fdw_option_name_not_found - 'HV00K', // fdw_reply_handle - 'HV00Q', // fdw_schema_not_found - 'HV00R', // fdw_table_not_found - 'HV00L', // fdw_unable_to_create_execution - 'HV00M', // fdw_unable_to_create_reply - 'HV00N', // fdw_unable_to_establish_connection - 'P0000', // plpgsql_error - 'P0001', // raise_exception - 'P0002', // no_data_found - 'P0003', // too_many_rows - 'P0004', // assert_failure - 'XX000', // internal_error - 'XX001', // data_corrupted - 'XX002', // index_corrupted -]; +export const POSTGRESQL_ERROR_CODES = { + SUCCESSFUL_COMPLETION: '00000', + WARNING: '01000', + DYNAMIC_RESULT_SETS_RETURNED: '0100C', + IMPLICIT_ZERO_BIT_PADDING: '01008', + NULL_VALUE_ELIMINATED_IN_SET_FUNCTION: '01003', + PRIVILEGE_NOT_GRANTED: '01007', + PRIVILEGE_NOT_REVOKED: '01006', + STRING_DATA_RIGHT_TRUNCATION: '01004', + DEPRECATED_FEATURE: '01P01', + NO_DATA: '02000', + NO_ADDITIONAL_DYNAMIC_RESULT_SETS_RETURNED: '02001', + SQL_STATEMENT_NOT_YET_COMPLETE: '03000', + CONNECTION_EXCEPTION: '08000', + CONNECTION_DOES_NOT_EXIST: '08003', + CONNECTION_FAILURE: '08006', + SQLCLIENT_UNABLE_TO_ESTABLISH_SQLCONNECTION: '08001', + SQLSERVER_REJECTED_ESTABLISHMENT_OF_SQLCONNECTION: '08004', + TRANSACTION_RESOLUTION_UNKNOWN: '08007', + PROTOCOL_VIOLATION: '08P01', + TRIGGERED_ACTION_EXCEPTION: '09000', + FEATURE_NOT_SUPPORTED: '0A000', + INVALID_TRANSACTION_INITIATION: '0B000', + LOCATOR_EXCEPTION: '0F000', + INVALID_LOCATOR_SPECIFICATION: '0F001', + INVALID_GRANTOR: '0L000', + INVALID_GRANT_OPERATION: '0LP01', + INVALID_ROLE_SPECIFICATION: '0P000', + DIAGNOSTICS_EXCEPTION: '0Z000', + STACKED_DIAGNOSTICS_ACCESSED_WITHOUT_ACTIVE_HANDLER: '0Z002', + CASE_NOT_FOUND: '20000', + CARDINALITY_VIOLATION: '21000', + DATA_EXCEPTION: '22000', + ARRAY_SUBSCRIPT_ERROR: '2202E', + CHARACTER_NOT_IN_REPERTOIRE: '22021', + DATETIME_FIELD_OVERFLOW: '22008', + DIVISION_BY_ZERO: '22012', + ERROR_IN_ASSIGNMENT: '22005', + ESCAPE_CHARACTER_CONFLICT: '2200B', + INDICATOR_OVERFLOW: '22022', + INTERVAL_FIELD_OVERFLOW: '22015', + INVALID_ARGUMENT_FOR_LOGARITHM: '2201E', + INVALID_ARGUMENT_FOR_NTILE_FUNCTION: '22014', + INVALID_ARGUMENT_FOR_NTH_VALUE_FUNCTION: '22016', + INVALID_ARGUMENT_FOR_POWER_FUNCTION: '2201F', + INVALID_ARGUMENT_FOR_WIDTH_BUCKET_FUNCTION: '2201G', + INVALID_CHARACTER_VALUE_FOR_CAST: '22018', + INVALID_DATETIME_FORMAT: '22007', + INVALID_ESCAPE_CHARACTER: '22019', + INVALID_ESCAPE_OCTET: '2200D', + INVALID_ESCAPE_SEQUENCE: '22025', + NONSTANDARD_USE_OF_ESCAPE_CHARACTER: '22P06', + INVALID_INDICATOR_PARAMETER_VALUE: '22010', + INVALID_PARAMETER_VALUE: '22023', + INVALID_PRECEDING_OR_FOLLOWING_SIZE: '22013', + INVALID_REGULAR_EXPRESSION: '2201B', + INVALID_ROW_COUNT_IN_LIMIT_CLAUSE: '2201W', + INVALID_ROW_COUNT_IN_RESULT_OFFSET_CLAUSE: '2201X', + INVALID_TABLESAMPLE_ARGUMENT: '2202H', + INVALID_TABLESAMPLE_REPEAT: '2202G', + INVALID_TIME_ZONE_DISPLACEMENT_VALUE: '22009', + INVALID_USE_OF_ESCAPE_CHARACTER: '2200C', + MOST_SPECIFIC_TYPE_MISMATCH: '2200G', + NULL_VALUE_NOT_ALLOWED: '22004', + NULL_VALUE_NO_INDICATOR_PARAMETER: '22002', + NUMERIC_VALUE_OUT_OF_RANGE: '22003', + SEQUENCE_GENERATOR_LIMIT_EXCEEDED: '2200H', + STRING_DATA_LENGTH_MISMATCH: '22026', + SUBSTRING_ERROR: '22011', + TRIM_ERROR: '22027', + UNTERMINATED_C_STRING: '22024', + ZERO_LENGTH_CHARACTER_STRING: '2200F', + FLOATING_POINT_EXCEPTION: '22P01', + INVALID_TEXT_REPRESENTATION: '22P02', + INVALID_BINARY_REPRESENTATION: '22P03', + BAD_COPY_FILE_FORMAT: '22P04', + UNTRANSLATABLE_CHARACTER: '22P05', + NOT_AN_XML_DOCUMENT: '2200L', + INVALID_XML_DOCUMENT: '2200M', + INVALID_XML_CONTENT: '2200N', + INVALID_XML_COMMENT: '2200S', + INVALID_XML_PROCESSING_INSTRUCTION: '2200T', + DUPLICATE_JSON_OBJECT_KEY_VALUE: '22030', + INVALID_ARGUMENT_FOR_SQL_JSON_DATETIME_FUNCTION: '22031', + INVALID_JSON_TEXT: '22032', + INVALID_SQL_JSON_SUBSCRIPT: '22033', + MORE_THAN_ONE_SQL_JSON_ITEM: '22034', + NO_SQL_JSON_ITEM: '22035', + NON_NUMERIC_SQL_JSON_ITEM: '22036', + NON_UNIQUE_KEYS_IN_A_JSON_OBJECT: '22037', + SINGLETON_SQL_JSON_ITEM_REQUIRED: '22038', + SQL_JSON_ARRAY_NOT_FOUND: '22039', + SQL_JSON_MEMBER_NOT_FOUND: '2203A', + SQL_JSON_NUMBER_NOT_FOUND: '2203B', + SQL_JSON_OBJECT_NOT_FOUND: '2203C', + TOO_MANY_JSON_ARRAY_ELEMENTS: '2203D', + TOO_MANY_JSON_OBJECT_MEMBERS: '2203E', + SQL_JSON_SCALAR_REQUIRED: '2203F', + SQL_JSON_ITEM_CANNOT_BE_CAST_TO_TARGET_TYPE: '2203G', + INTEGRITY_CONSTRAINT_VIOLATION: '23000', + RESTRICT_VIOLATION: '23001', + NOT_NULL_VIOLATION: '23502', + FOREIGN_KEY_VIOLATION: '23503', + UNIQUE_VIOLATION: '23505', + CHECK_VIOLATION: '23514', + EXCLUSION_VIOLATION: '23P01', + INVALID_CURSOR_STATE: '24000', + INVALID_TRANSACTION_STATE: '25000', + ACTIVE_SQL_TRANSACTION: '25001', + BRANCH_TRANSACTION_ALREADY_ACTIVE: '25002', + HELD_CURSOR_REQUIRES_SAME_ISOLATION_LEVEL: '25008', + INAPPROPRIATE_ACCESS_MODE_FOR_BRANCH_TRANSACTION: '25003', + INAPPROPRIATE_ISOLATION_LEVEL_FOR_BRANCH_TRANSACTION: '25004', + NO_ACTIVE_SQL_TRANSACTION_FOR_BRANCH_TRANSACTION: '25005', + READ_ONLY_SQL_TRANSACTION: '25006', + SCHEMA_AND_DATA_STATEMENT_MIXING_NOT_SUPPORTED: '25007', + NO_ACTIVE_SQL_TRANSACTION: '25P01', + IN_FAILED_SQL_TRANSACTION: '25P02', + IDLE_IN_TRANSACTION_SESSION_TIMEOUT: '25P03', + TRANSACTION_TIMEOUT: '25P04', + INVALID_SQL_STATEMENT_NAME: '26000', + TRIGGERED_DATA_CHANGE_VIOLATION: '27000', + INVALID_AUTHORIZATION_SPECIFICATION: '28000', + INVALID_PASSWORD: '28P01', + DEPENDENT_PRIVILEGE_DESCRIPTORS_STILL_EXIST: '2B000', + DEPENDENT_OBJECTS_STILL_EXIST: '2BP01', + INVALID_TRANSACTION_TERMINATION: '2D000', + SQL_ROUTINE_EXCEPTION: '2F000', + FUNCTION_EXECUTED_NO_RETURN_STATEMENT: '2F005', + MODIFYING_SQL_DATA_NOT_PERMITTED: '2F002', + PROHIBITED_SQL_STATEMENT_ATTEMPTED: '2F003', + READING_SQL_DATA_NOT_PERMITTED: '2F004', + INVALID_CURSOR_NAME: '34000', + EXTERNAL_ROUTINE_EXCEPTION: '38000', + CONTAINING_SQL_NOT_PERMITTED: '38001', + EXTERNAL_MODIFYING_SQL_DATA_NOT_PERMITTED: '38002', + EXTERNAL_PROHIBITED_SQL_STATEMENT_ATTEMPTED: '38003', + EXTERNAL_READING_SQL_DATA_NOT_PERMITTED: '38004', + EXTERNAL_ROUTINE_INVOCATION_EXCEPTION: '39000', + INVALID_SQLSTATE_RETURNED: '39001', + EXTERNAL_NULL_VALUE_NOT_ALLOWED: '39004', + TRIGGER_PROTOCOL_VIOLATED: '39P01', + SRF_PROTOCOL_VIOLATED: '39P02', + EVENT_TRIGGER_PROTOCOL_VIOLATED: '39P03', + SAVEPOINT_EXCEPTION: '3B000', + INVALID_SAVEPOINT_SPECIFICATION: '3B001', + INVALID_CATALOG_NAME: '3D000', + INVALID_SCHEMA_NAME: '3F000', + TRANSACTION_ROLLBACK: '40000', + TRANSACTION_INTEGRITY_CONSTRAINT_VIOLATION: '40002', + SERIALIZATION_FAILURE: '40001', + STATEMENT_COMPLETION_UNKNOWN: '40003', + DEADLOCK_DETECTED: '40P01', + SYNTAX_ERROR_OR_ACCESS_RULE_VIOLATION: '42000', + SYNTAX_ERROR: '42601', + INSUFFICIENT_PRIVILEGE: '42501', + CANNOT_COERCE: '42846', + GROUPING_ERROR: '42803', + WINDOWING_ERROR: '42P20', + INVALID_RECURSION: '42P19', + INVALID_FOREIGN_KEY: '42830', + INVALID_NAME: '42602', + NAME_TOO_LONG: '42622', + RESERVED_NAME: '42939', + DATATYPE_MISMATCH: '42804', + INDETERMINATE_DATATYPE: '42P18', + COLLATION_MISMATCH: '42P21', + INDETERMINATE_COLLATION: '42P22', + WRONG_OBJECT_TYPE: '42809', + GENERATED_ALWAYS: '428C9', + UNDEFINED_COLUMN: '42703', + UNDEFINED_FUNCTION: '42883', + UNDEFINED_TABLE: '42P01', + UNDEFINED_PARAMETER: '42P02', + UNDEFINED_OBJECT: '42704', + DUPLICATE_COLUMN: '42701', + DUPLICATE_CURSOR: '42P03', + DUPLICATE_DATABASE: '42P04', + DUPLICATE_FUNCTION: '42723', + DUPLICATE_PREPARED_STATEMENT: '42P05', + DUPLICATE_SCHEMA: '42P06', + DUPLICATE_TABLE: '42P07', + DUPLICATE_ALIAS: '42712', + DUPLICATE_OBJECT: '42710', + AMBIGUOUS_COLUMN: '42702', + AMBIGUOUS_FUNCTION: '42725', + AMBIGUOUS_PARAMETER: '42P08', + AMBIGUOUS_ALIAS: '42P09', + INVALID_COLUMN_REFERENCE: '42P10', + INVALID_COLUMN_DEFINITION: '42611', + INVALID_CURSOR_DEFINITION: '42P11', + INVALID_DATABASE_DEFINITION: '42P12', + INVALID_FUNCTION_DEFINITION: '42P13', + INVALID_PREPARED_STATEMENT_DEFINITION: '42P14', + INVALID_SCHEMA_DEFINITION: '42P15', + INVALID_TABLE_DEFINITION: '42P16', + INVALID_OBJECT_DEFINITION: '42P17', + WITH_CHECK_OPTION_VIOLATION: '44000', + INSUFFICIENT_RESOURCES: '53000', + DISK_FULL: '53100', + OUT_OF_MEMORY: '53200', + TOO_MANY_CONNECTIONS: '53300', + CONFIGURATION_LIMIT_EXCEEDED: '53400', + PROGRAM_LIMIT_EXCEEDED: '54000', + STATEMENT_TOO_COMPLEX: '54001', + TOO_MANY_COLUMNS: '54011', + TOO_MANY_ARGUMENTS: '54023', + OBJECT_NOT_IN_PREREQUISITE_STATE: '55000', + OBJECT_IN_USE: '55006', + CANT_CHANGE_RUNTIME_PARAM: '55P02', + LOCK_NOT_AVAILABLE: '55P03', + UNSAFE_NEW_ENUM_VALUE_USAGE: '55P04', + OPERATOR_INTERVENTION: '57000', + QUERY_CANCELED: '57014', + ADMIN_SHUTDOWN: '57P01', + CRASH_SHUTDOWN: '57P02', + CANNOT_CONNECT_NOW: '57P03', + DATABASE_DROPPED: '57P04', + IDLE_SESSION_TIMEOUT: '57P05', + SYSTEM_ERROR: '58000', + IO_ERROR: '58030', + UNDEFINED_FILE: '58P01', + DUPLICATE_FILE: '58P02', + CONFIG_FILE_ERROR: 'F0000', + LOCK_FILE_EXISTS: 'F0001', + FDW_ERROR: 'HV000', + FDW_COLUMN_NAME_NOT_FOUND: 'HV005', + FDW_DYNAMIC_PARAMETER_VALUE_NEEDED: 'HV002', + FDW_FUNCTION_SEQUENCE_ERROR: 'HV010', + FDW_INCONSISTENT_DESCRIPTOR_INFORMATION: 'HV021', + FDW_INVALID_ATTRIBUTE_VALUE: 'HV024', + FDW_INVALID_COLUMN_NAME: 'HV007', + FDW_INVALID_COLUMN_NUMBER: 'HV008', + FDW_INVALID_DATA_TYPE: 'HV004', + FDW_INVALID_DATA_TYPE_DESCRIPTORS: 'HV006', + FDW_INVALID_DESCRIPTOR_FIELD_IDENTIFIER: 'HV091', + FDW_INVALID_HANDLE: 'HV00B', + FDW_INVALID_OPTION_INDEX: 'HV00C', + FDW_INVALID_OPTION_NAME: 'HV00D', + FDW_INVALID_STRING_LENGTH_OR_BUFFER_LENGTH: 'HV090', + FDW_INVALID_STRING_FORMAT: 'HV00A', + FDW_INVALID_USE_OF_NULL_POINTER: 'HV009', + FDW_TOO_MANY_HANDLES: 'HV014', + FDW_OUT_OF_MEMORY: 'HV001', + FDW_NO_SCHEMAS: 'HV00P', + FDW_OPTION_NAME_NOT_FOUND: 'HV00J', + FDW_REPLY_HANDLE: 'HV00K', + FDW_SCHEMA_NOT_FOUND: 'HV00Q', + FDW_TABLE_NOT_FOUND: 'HV00R', + FDW_UNABLE_TO_CREATE_EXECUTION: 'HV00L', + FDW_UNABLE_TO_CREATE_REPLY: 'HV00M', + FDW_UNABLE_TO_ESTABLISH_CONNECTION: 'HV00N', + PLPGSQL_ERROR: 'P0000', + RAISE_EXCEPTION: 'P0001', + NO_DATA_FOUND: 'P0002', + TOO_MANY_ROWS: 'P0003', + ASSERT_FAILURE: 'P0004', + INTERNAL_ERROR: 'XX000', + DATA_CORRUPTED: 'XX001', + INDEX_CORRUPTED: 'XX002', +}; diff --git a/packages/twenty-server/src/engine/api/rest/utils/workspace-query-runner-rest-api-exception-handler.util.ts b/packages/twenty-server/src/engine/api/rest/utils/workspace-query-runner-rest-api-exception-handler.util.ts index 14d3f504aa..e7be96f8f9 100644 --- a/packages/twenty-server/src/engine/api/rest/utils/workspace-query-runner-rest-api-exception-handler.util.ts +++ b/packages/twenty-server/src/engine/api/rest/utils/workspace-query-runner-rest-api-exception-handler.util.ts @@ -7,6 +7,10 @@ import { commonQueryRunnerToRestApiExceptionHandler } from 'src/engine/api/commo import { RestInputRequestParserException } from 'src/engine/api/rest/input-request-parsers/rest-input-request-parser.exception'; import { ThrottlerException } from 'src/engine/core-modules/throttler/throttler.exception'; import { throttlerToRestApiExceptionHandler } from 'src/engine/core-modules/throttler/utils/throttler-to-rest-api-exception-handler.util'; +import { + TwentyORMException, + TwentyORMExceptionCode, +} from 'src/engine/twenty-orm/exceptions/twenty-orm.exception'; interface QueryFailedErrorWithCode extends QueryFailedError { code: string; @@ -22,6 +26,9 @@ export const workspaceQueryRunnerRestApiExceptionHandler = ( throw new BadRequestException(error.message); case error instanceof ThrottlerException: return throttlerToRestApiExceptionHandler(error); + case error instanceof TwentyORMException && + error.code === TwentyORMExceptionCode.INVALID_INPUT: + throw new BadRequestException(error.message); default: throw error; } diff --git a/packages/twenty-server/src/engine/twenty-orm/error-handling/compute-twenty-orm-exception.ts b/packages/twenty-server/src/engine/twenty-orm/error-handling/compute-twenty-orm-exception.ts index ea71e268c8..f9fad80e6b 100644 --- a/packages/twenty-server/src/engine/twenty-orm/error-handling/compute-twenty-orm-exception.ts +++ b/packages/twenty-server/src/engine/twenty-orm/error-handling/compute-twenty-orm-exception.ts @@ -35,10 +35,10 @@ export const computeTwentyORMException = async ( ); } + const errorCode = (error as QueryFailedErrorWithCode).code; + if ( - error.message.includes( - 'duplicate key value violates unique constraint', - ) && + errorCode === POSTGRESQL_ERROR_CODES.UNIQUE_VIOLATION && isDefined(objectMetadata) && isDefined(entityManager) && isDefined(internalContext) @@ -51,17 +51,18 @@ export const computeTwentyORMException = async ( ); } - if (error.message.includes('invalid input value for')) { + if (errorCode === POSTGRESQL_ERROR_CODES.INVALID_TEXT_REPRESENTATION) { return new TwentyORMException( - error.message, + error.message, // safe and useful TwentyORMExceptionCode.INVALID_INPUT, ); } - const errorCode = (error as QueryFailedErrorWithCode).code; - - if (isDefined(errorCode) && POSTGRESQL_ERROR_CODES.includes(errorCode)) { - throw new PostgresException(error.message, errorCode); + if ( + isDefined(errorCode) && + Object.values(POSTGRESQL_ERROR_CODES).includes(errorCode) + ) { + throw new PostgresException('Data validation error.', errorCode); } throw error; } diff --git a/packages/twenty-server/src/modules/messaging/message-import-manager/crons/commands/messaging-messages-import.cron.command.ts b/packages/twenty-server/src/modules/messaging/message-import-manager/crons/commands/messaging-messages-import.cron.command.ts index c5a3c88a22..d29be4a92b 100644 --- a/packages/twenty-server/src/modules/messaging/message-import-manager/crons/commands/messaging-messages-import.cron.command.ts +++ b/packages/twenty-server/src/modules/messaging/message-import-manager/crons/commands/messaging-messages-import.cron.command.ts @@ -25,9 +25,7 @@ export class MessagingMessagesImportCronCommand extends CommandRunner { jobName: MessagingMessagesImportCronJob.name, data: undefined, options: { - repeat: { - pattern: MESSAGING_MESSAGES_IMPORT_CRON_PATTERN, - }, + repeat: { pattern: MESSAGING_MESSAGES_IMPORT_CRON_PATTERN }, }, }); } diff --git a/packages/twenty-server/test/integration/graphql/suites/inputs-validation/create-validation/__snapshots__/date-field-create-input-validation.integration-spec.ts.snap b/packages/twenty-server/test/integration/graphql/suites/inputs-validation/create-validation/__snapshots__/date-field-create-input-validation.integration-spec.ts.snap index bbcc4bed59..9ef4f4fff3 100644 --- a/packages/twenty-server/test/integration/graphql/suites/inputs-validation/create-validation/__snapshots__/date-field-create-input-validation.integration-spec.ts.snap +++ b/packages/twenty-server/test/integration/graphql/suites/inputs-validation/create-validation/__snapshots__/date-field-create-input-validation.integration-spec.ts.snap @@ -6,7 +6,7 @@ exports[`Create input validation - DATE Gql create input - failure DATE - should exports[`Create input validation - DATE Gql create input - failure DATE - should fail with : {"dateField":{}} 1`] = `"Invalid value {} for date or date-time field "dateField""`; -exports[`Create input validation - DATE Gql create input - failure DATE - should fail with : {"dateField":1} 1`] = `"invalid input syntax for type date: "1""`; +exports[`Create input validation - DATE Gql create input - failure DATE - should fail with : {"dateField":1} 1`] = `"Data validation error."`; exports[`Create input validation - DATE Gql create input - failure DATE - should fail with : {"dateField":true} 1`] = `"Invalid value true for date or date-time field "dateField""`; @@ -16,6 +16,6 @@ exports[`Create input validation - DATE Rest create input - failure DATE - shoul exports[`Create input validation - DATE Rest create input - failure DATE - should fail with : {"dateField":{}} 1`] = `"["Invalid value {} for date or date-time field \\"dateField\\""]"`; -exports[`Create input validation - DATE Rest create input - failure DATE - should fail with : {"dateField":1} 1`] = `"["invalid input syntax for type date: \\"1\\""]"`; +exports[`Create input validation - DATE Rest create input - failure DATE - should fail with : {"dateField":1} 1`] = `"["Data validation error."]"`; exports[`Create input validation - DATE Rest create input - failure DATE - should fail with : {"dateField":true} 1`] = `"["Invalid value true for date or date-time field \\"dateField\\""]"`; diff --git a/packages/twenty-server/test/integration/graphql/suites/inputs-validation/filter-validation/constants/failing-filter-input-by-field-metadata-type.constant.ts b/packages/twenty-server/test/integration/graphql/suites/inputs-validation/filter-validation/constants/failing-filter-input-by-field-metadata-type.constant.ts index b1b3cf8fe3..59bc80f562 100644 --- a/packages/twenty-server/test/integration/graphql/suites/inputs-validation/filter-validation/constants/failing-filter-input-by-field-metadata-type.constant.ts +++ b/packages/twenty-server/test/integration/graphql/suites/inputs-validation/filter-validation/constants/failing-filter-input-by-field-metadata-type.constant.ts @@ -25,8 +25,7 @@ export const failingFilterInputByFieldMetadataType: { gqlErrorMessage: 'is not defined by type', restFilterInput: 'manyToOneRelationField[eq]:"6dd71a46-68fe-4420-82b3-0d5b00ad2642"', - restErrorMessage: - 'column apiInputValidationTestObject.manyToOneRelationField does not exist', + restErrorMessage: 'Data validation error', }, { gqlFilterInput: { @@ -78,7 +77,7 @@ export const failingFilterInputByFieldMetadataType: { }, gqlErrorMessage: 'is not defined by type', restFilterInput: `${[joinColumnNameForManyToOneMorphRelationField1.replace('Id', '')]}[eq]:"6dd71a46-68fe-4420-82b3-0d5b00ad2642"`, - restErrorMessage: `column apiInputValidationTestObject.${joinColumnNameForManyToOneMorphRelationField1.replace('Id', '')} does not exist`, + restErrorMessage: `Data validation error`, }, ], [FieldMetadataType.UUID]: [ @@ -147,27 +146,21 @@ export const failingFilterInputByFieldMetadataType: { [FieldMetadataType.DATE_TIME]: [ { gqlFilterInput: { dateTimeField: { eq: 'not-a-date-time' } }, - gqlErrorMessage: - 'invalid input syntax for type timestamp with time zone: "not-a-date-time"', + gqlErrorMessage: 'Data validation error.', restFilterInput: 'dateTimeField[eq]:"not-a-date-time"', - restErrorMessage: - 'invalid input syntax for type timestamp with time zone', + restErrorMessage: 'Data validation error.', }, { gqlFilterInput: { dateTimeField: { eq: {} } }, - gqlErrorMessage: - 'invalid input syntax for type timestamp with time zone: "{}"', + gqlErrorMessage: 'Data validation error.', restFilterInput: 'dateTimeField[eq]:"{}"', - restErrorMessage: - 'invalid input syntax for type timestamp with time zone', + restErrorMessage: 'Data validation error.', }, { gqlFilterInput: { dateTimeField: { eq: [] } }, - gqlErrorMessage: - 'invalid input syntax for type timestamp with time zone: "{}"', + gqlErrorMessage: 'Data validation error.', restFilterInput: 'dateTimeField[eq]:"[]"', - restErrorMessage: - 'invalid input syntax for type timestamp with time zone', + restErrorMessage: 'Data validation error.', }, // TODO - fix this, should throw an error // { @@ -179,21 +172,21 @@ export const failingFilterInputByFieldMetadataType: { [FieldMetadataType.DATE]: [ { gqlFilterInput: { dateField: { eq: 'not-a-date' } }, - gqlErrorMessage: 'invalid input syntax for type date: "not-a-date"', + gqlErrorMessage: 'Data validation error', restFilterInput: 'dateField[eq]:"{}"', - restErrorMessage: 'invalid input syntax for type date', + restErrorMessage: 'Data validation error', }, { gqlFilterInput: { dateField: { eq: {} } }, - gqlErrorMessage: 'invalid input syntax for type date: "{}"', + gqlErrorMessage: 'Data validation error', restFilterInput: 'dateField[eq]:"{}"', - restErrorMessage: 'invalid input syntax for type date', + restErrorMessage: 'Data validation error', }, { gqlFilterInput: { dateField: { eq: [] } }, - gqlErrorMessage: 'invalid input syntax for type date: "{}"', + gqlErrorMessage: 'Data validation error', restFilterInput: 'dateField[eq]:"[]"', - restErrorMessage: 'invalid input syntax for type date', + restErrorMessage: 'Data validation error', }, ], [FieldMetadataType.BOOLEAN]: [