NetLife Guru

Open source Go packages for fast, maintainable web systems. Built with a documentation-first approach.

Product
OverviewGolang packagesNews
Documentation
DocumentationGo LoggerGo RouterGo DB Form
Company
OverviewContactNewsGitHub
Community / Support
Supportinfo@netlife.guru
© 2026 NetLife Guru. All rights reserved.
GitHubinfo@netlife.guru
NetLife GuruNetLife GuruNetLife Guru
NetLife GuruNetLife GuruNetLife Guru
OverviewDocumentationNewsSupportContact

Golang packages

About
ScanningMaps and RowsConvertersCacheErrors
MappingDynamic RowsEdge Cases
MapperAPI

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
  • ScanStructOne row count checks

Overview

ErrorMeaning
ErrNoRowsScanStructOne did not receive any rows
ErrTooManyRowsScanStructOne 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, nil

Callback 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, nil

Required 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, nil

Unique 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

Cache

API reference for named scan-plan caching and repeated struct scanning.

Mapping

Learn how mapper matches database columns to Go struct fields.

On this page

OverviewErrNoRowsErrTooManyRowsCallback ErrorsRow ErrorsColumn ErrorsAssignment ErrorsFillFromMap ErrorsConverter FailuresRecommended Error HandlingCommon PatternsOptional LookupRequired LookupUnique Lookup