GORM: How to check if an error is of type Duplicate Key

In GORM you can easily check if an error is of type Duplicate Key. This will allow you to handle situations of duplicate records insertion more gracefully.

GORM is one of the most popular ORM library available for golang. We have extensively used GORM for our services at scale and I can say that it has been battle tested. Recently, I came across a situation where we had to handle a case of duplicate records insertion gracefully and in order to do I had to check if an error returned by GORM is of type DUPLICATE_KEY.

Let me give you some more context on the problem statement

Lets say you have a table named user_games which is a mapping table that stores if an userId is joining a particular game (FOOTBALL, CRICKET, etc.) or not. As, a person can only join a game only once therefore we have an unique constraint on userId, gameId combination.

Now, entries in this table can happen via two ways:

  1. Your friend can add you to a game and
  2. You can add yourself. We want to show an error message of the form “XXX already part of the game”, when user tries to add this mapping either for self or for a friend.

In order to achieve the above behaviour, we can capture the error returned while inserting a record into the table and check if its of type DUPLICATE_KEY. If yes, then we can use this information to generate an error message to be displayed to the user.

Check if gorm error if of type DUPLICATE_KEY

Now, we already what we have to do. In order to solve the above defined problem statement, all we have to do is check if the gorm error is of type DUPLICATE_KEY and return response accordingly. Let try to do so:

type DBError string

const (
  DuplicateKey DBError = "DUPLICATE_KEY"
  UnknownError DBError = "UNKNOWN"

func getErrorType(err error) {

  // the err above is the error returned when GORM code is executed
  // It represents the Error field in the response returned from gorm
  // Example: res := db.Model(xyz{}).Save(&obj) // res.Error

  pgErr := err.(*pgconn.PgError)

  switch pgErr.Code {
    case "23505": // In PG the error code is 23505 for DUPLICATE_KEY
      return DuplicateKey
      return UnknownError

Let’s understand the code written above. We are casting the error returned from the execution of the GORM query into pgError type. Since, we are using PostgreSQL as our underlying database, hence I am casting into that. If you are using MySQL or any other DB, please cast into its corresponding error type.

Note: For MySQL please use the command below

mysqlErr := err.(*mysql.MySQLError)
case 1062: // In mySQL the errorCode is 1062
  return DuplicateKey

So, this is it. We now have a generic implementation to handle GORM errors of type DUPLICATE_KEY. I am saying generic for two reasons.

  1. If tomorrow you move away from PG. All you have to do is change the error type and codes.
  2. You can handle errors of different type with this approach.