{"record":{"id":"5b3c097695f5172a","repo":"go-gorm/gorm","slug":"record-not-found","errorCode":null,"errorMessage":"record not found","messagePattern":"record not found","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"logger/logger.go","lineNumber":17,"sourceCode":"// Package logger provides a logger interface and its implementation for GORM.\npackage logger\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"log\"\n\t\"os\"\n\t\"time\"\n\n\t\"gorm.io/gorm/utils\"\n)\n\n// ErrRecordNotFound record not found error\nvar ErrRecordNotFound = errors.New(\"record not found\")\n\n// Colors\nconst (\n\tReset       = \"\\033[0m\"\n\tRed         = \"\\033[31m\"\n\tGreen       = \"\\033[32m\"\n\tYellow      = \"\\033[33m\"\n\tBlue        = \"\\033[34m\"\n\tMagenta     = \"\\033[35m\"\n\tCyan        = \"\\033[36m\"\n\tWhite       = \"\\033[37m\"\n\tBlueBold    = \"\\033[34;1m\"\n\tMagentaBold = \"\\033[35;1m\"\n\tRedBold     = \"\\033[31;1m\"\n\tYellowBold  = \"\\033[33;1m\"\n)\n\n// LogLevel log level","sourceCodeStart":1,"sourceCodeEnd":35,"githubUrl":"https://github.com/go-gorm/gorm/blob/1d6ce99528060be18a42be09aca8d39efcb47f28/logger/logger.go#L1-L35","documentation":"logger.ErrRecordNotFound is the sentinel error 'record not found' defined in the logger package (mirroring gorm.ErrRecordNotFound). It is returned by First/Take/Last finishers when the query matched zero rows and the Statement has the RecordNotFound condition set. It is not a driver error - GORM synthesizes it after RowsAffected == 0.","triggerScenarios":"Calling db.First(&user, id), db.Take, or db.Last on a table where no row matches the WHERE clause; also Session with AllowGlobalUpdate off does not matter here - any First-family read returning 0 rows yields this error.","commonSituations":"Looking up a record by an ID that was deleted or never existed; filtering with conditions that match nothing (typo'd status value, soft-deleted rows hidden by a soft_delete plugin); race where another request deleted the row between check and read; using Find semantics (which do NOT error) vs First semantics (which do) inconsistently.","solutions":["Treat ErrRecordNotFound as an expected branch: check errors.Is(err, gorm.ErrRecordNotFound) and return a 404/empty result instead of a 500.","Verify the ID/conditions actually match a row (row may be soft-deleted - query with Unscoped if recovery UI needs it).","If an empty result is normal for your flow, use Find(&users) which returns no error for zero rows.","For race-prone reads, wrap the read+act in a transaction or use upsert (clause.OnConflict) instead of read-then-insert."],"exampleFix":"// before\nerr := db.First(&user, id).Error\nif err != nil { return 500 }\n\n// after\nerr := db.First(&user, id).Error\nif errors.Is(err, gorm.ErrRecordNotFound) {\n    return ErrNotFound // 404 to caller\n}\nif err != nil { return err }","handlingStrategy":"try-catch","validationCode":null,"typeGuard":"func isNotFound(err error) bool { return errors.Is(err, gorm.ErrRecordNotFound) }","tryCatchPattern":"err := db.First(&user, id).Error\nswitch {\ncase err == nil:\ncase errors.Is(err, gorm.ErrRecordNotFound):\n    return ErrNotFound // expected: 404\ndefault:\n    return err // unexpected: 500\n}","preventionTips":["Never treat First's error as fatal without first excluding ErrRecordNotFound via errors.Is.","Use Find for 'zero rows is fine' flows; reserve First for 'must exist' flows.","In race-prone flows prefer upserts or transactions over read-then-act."],"tags":["gorm","query","not-found","sentinel-error"],"backgroundTag":null,"analyzedSha":"1d6ce99528060be18a42be09aca8d39efcb47f28","analyzedAt":"2026-08-15T13:01:23.433Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}