Errors
API reference for mapper errors and recommended error handling patterns.
Mapper returns errors when row scanning, column loading, field assignment, or single-row expectations fail.
Most errors come from one of these sources:
- the database rows implementation
- invalid scan destinations
- unsupported type assignments
- callback errors
ScanStructOnerow count checks
Overview
| Error | Meaning |
|---|---|
ErrNoRows | ScanStructOne did not receive any rows |
ErrTooManyRows | ScanStructOne received more than one row |
Use errors.Is when checking mapper sentinel errors.
if errors.Is(err, mapper.ErrNoRows) {
return nil
}ErrNoRows
ErrNoRows is returned by ScanStructOne when the result set is empty.
user, err := mapper.ScanStructOne[User](rows)
if errors.Is(err, mapper.ErrNoRows) {
return nil
}
if err != nil {
return err
}
fmt.Println(user.Name)Use this when an empty result is valid and should be handled differently from a real failure.
Example:
func FindUser(db *sql.DB, id string) (*User, error) {
rows, err := db.Query(`
SELECT id, name, email
FROM users
WHERE id = ?
LIMIT 1
`, id)
if err != nil {
return nil, err
}
defer rows.Close()
user, err := mapper.ScanStructOne[User](rows)
if errors.Is(err, mapper.ErrNoRows) {
return nil, nil
}
if err != nil {
return nil, err
}
return user, nil
}ErrTooManyRows
ErrTooManyRows is returned by ScanStructOne when the result set contains more than one row.
user, err := mapper.ScanStructOne[User](rows)
if errors.Is(err, mapper.ErrTooManyRows) {
return errors.New("expected only one user")
}
if err != nil {
return err
}This usually means the query is missing a unique condition, LIMIT 1, or the database contains unexpected duplicate data.
Example:
rows, err := db.Query(`
SELECT id, name, email
FROM users
WHERE email = ?
`, email)
if err != nil {
return nil, err
}
defer rows.Close()
user, err := mapper.ScanStructOne[User](rows)
if errors.Is(err, mapper.ErrTooManyRows) {
return nil, errors.New("email matched multiple users")
}
if errors.Is(err, mapper.ErrNoRows) {
return nil, nil
}
if err != nil {
return nil, err
}
return user, nilCallback Errors
ScanStructRows and ScanMapRows stop scanning when the callback returns an error.
The same error is returned to the caller.
err := mapper.ScanStructRows[User](rows, func(user *User) error {
if user.ID == "" {
return errors.New("missing user id")
}
return nil
})
if err != nil {
return err
}This is useful for validation, early stopping, or forwarding downstream processing failures.
err := mapper.ScanStructRows[User](rows, func(user *User) error {
return indexUser(user)
})If indexUser returns an error, scanning stops and that error is returned.
Row Errors
Mapper checks row-level errors after scanning.
If the underlying rows object returns an error from Rows.Err(), mapper returns that error.
err := mapper.ScanStructRows[User](rows, func(user *User) error {
fmt.Println(user.Name)
return nil
})
if err != nil {
return err
}These errors usually come from the database driver.
Examples include:
- connection errors
- network interruption
- scan errors
- cursor errors
- driver-specific row iteration errors
Column Errors
Mapper reads column names using Rows.Columns().
If loading column names fails, mapper returns that error.
users, err := mapper.ScanStructSlice[User](rows)
if err != nil {
return err
}Column errors are uncommon, but they can happen if the underlying driver cannot expose column metadata.
Assignment Errors
Mapper returns an error when a scanned value cannot be assigned to the destination field.
Example:
type User struct {
CreatedAt time.Time `db:"created_at"`
}If the database returns a value that cannot be assigned to time.Time, scanning fails.
users, err := mapper.ScanStructSlice[User](rows)
if err != nil {
return err
}Assignment errors usually mean that:
- the database column type does not match the Go field type
- the struct field type is unsupported
- a JSON field contains invalid JSON
- a numeric value overflows the target type
- the destination field cannot be set
FillFromMap Errors
FillFromMap returns an error when the destination is invalid or a value cannot be assigned.
var user User
err := mapper.FillFromMap(&user, row)
if err != nil {
return err
}The destination must be a non-nil pointer to a struct.
// Correct
err := mapper.FillFromMap(&user, row)Invalid usage:
// Wrong: destination is not a pointer.
err := mapper.FillFromMap(user, row)var user *User
// Wrong: destination is nil.
err := mapper.FillFromMap(user, row)Converter Failures
Converter helpers such as AsString, AsInt64, AsBool, and AsTime do not return errors.
They return ok = false.
id, ok := mapper.AsInt64(row["id"])
if !ok {
return errors.New("invalid id")
}This lets you decide how strict your own custom mapping should be.
Recommended Error Handling
Use errors.Is for mapper sentinel errors.
user, err := mapper.ScanStructOne[User](rows)
if errors.Is(err, mapper.ErrNoRows) {
return nil
}
if errors.Is(err, mapper.ErrTooManyRows) {
return errors.New("expected one row")
}
if err != nil {
return err
}Return callback errors directly unless you need more context.
err := mapper.ScanStructRows[User](rows, func(user *User) error {
if err := validateUser(user); err != nil {
return fmt.Errorf("validate user %s: %w", user.ID, err)
}
return nil
})Add context around assignment or query-level failures when useful.
users, err := mapper.ScanStructSlice[User](rows)
if err != nil {
return nil, fmt.Errorf("scan users: %w", err)
}Common Patterns
Optional Lookup
user, err := mapper.ScanStructOne[User](rows)
if errors.Is(err, mapper.ErrNoRows) {
return nil, nil
}
if err != nil {
return nil, err
}
return user, nilRequired Lookup
user, err := mapper.ScanStructOne[User](rows)
if errors.Is(err, mapper.ErrNoRows) {
return nil, errors.New("user not found")
}
if err != nil {
return nil, err
}
return user, nilUnique Lookup
user, err := mapper.ScanStructOne[User](rows)
if errors.Is(err, mapper.ErrTooManyRows) {
return nil, errors.New("expected unique user")
}
if errors.Is(err, mapper.ErrNoRows) {
return nil, errors.New("user not found")
}
if err != nil {
return nil, err
}
return user, nil