25

The official documentation for GORM demonstrates a way in which one can test for the existence of a record, i.e.:

user := User{Name: "Jinzhu", Age: 18, Birthday: time.Now()}

// returns true if record hasn’t been saved (primary key `Id` is blank)
db.NewRecord(user) // => true

db.Create(&user)

// will return false after `user` created
db.NewRecord(user) // => false

This can be used to test indirectly for errors in record creation but reports no useful information in the event of a failure.

Having checked the source code for db.Create, there seems to be some sort of stack-frame inspection that checks for errors before proceeding, meaning that transactional errors will fail silently:

func Create(scope *Scope) {
    defer scope.Trace(NowFunc())

    if !scope.HasError() {
        // actually perform the transaction
    }
}
  • Is this a bug, or am I missing something?
  • How can/should I be informed of a failed transaction?
  • Where can I get useful debugging information?
Louis Thibault
  • 20,240
  • 25
  • 83
  • 152

3 Answers3

30

DB.Create() returns a new (cloned) gorm.DB which is a struct and has a field Error:

type DB struct {
    Value        interface{}
    Error        error
    RowsAffected int64
    // contains filtered or unexported fields
}

You can store the returned *gorm.DB value and check its DB.Error field like this:

if dbc := db.Create(&user); dbc.Error != nil {
    // Create failed, do something e.g. return, panic etc.
    return
}

If you don't need anything else from the returned gorm.DB, you can directly check its Error field:

if db.Create(&user).Error != nil {
    // Create failed, do something e.g. return, panic etc.
    return
}
icza
  • 389,944
  • 63
  • 907
  • 827
  • 3
    This error checking doesn't work at least with current gorm. See answer by @windyzboy and my comment there. – gavv Aug 29 '16 at 05:58
  • 1
    @gavv You're right, this doesn't work anymore. I tried to delete the answer, but cannot (because it's accepted), so I just edit it. – icza Aug 29 '16 at 07:43
  • Works with v1.9.10. – COil Sep 11 '19 at 16:09
19

I have tried the accepted answer, but it doesn't work, db.Error always return nil.

Just change something and it works, hope it helps somebody:

if err := db.Create(&Animal{Name: "Giraffe"}).Error; err != nil {
   // Create failed, do something e.g. return, panic etc.
   return 
}
windyzboy
  • 1,358
  • 16
  • 20
  • 7
    Correct. The accepted answer doesn't work because `Create` returns a clone of `DB` and `Error` field is set in that clone instead of original object. – gavv Aug 29 '16 at 05:57
0

If you want to check type of error, just do it.

if err := db.Create(&user).Error; err != nil {

  if errors.Is(err, gorm.ErrRecordNotFound) {
    fmt.Println(err.Error())
  }

  return

}