jackc/pgx · error

too many rows in result set

Error message

too many rows in result set

What it means

ErrTooManyRows is a package-level sentinel (errors.New at conn.go:110) raised by CollectExactlyOneRow when a second row is available after scanning the first. It signals that a query expected to match exactly one row matched more than one — a data-uniqueness violation rather than a protocol error. QueryRow itself silently keeps the first row, so this sentinel is specific to the CollectExactlyOneRow helper.

Source

Thrown at conn.go:110

// Identifier a PostgreSQL identifier or name. Identifiers can be composed of
// multiple parts such as ["schema", "table"] or ["table", "column"].
type Identifier []string

// Sanitize returns a sanitized string safe for SQL interpolation.
func (ident Identifier) Sanitize() string {
	parts := make([]string, len(ident))
	for i := range ident {
		s := strings.ReplaceAll(ident[i], string([]byte{0}), "")
		parts[i] = `"` + strings.ReplaceAll(s, `"`, `""`) + `"`
	}
	return strings.Join(parts, ".")
}

var (
	// ErrNoRows occurs when rows are expected but none are returned.
	ErrNoRows = newProxyErr(sql.ErrNoRows, "no rows in result set")
	// ErrTooManyRows occurs when more rows than expected are returned.
	ErrTooManyRows = errors.New("too many rows in result set")
)

func newProxyErr(background error, msg string) error {
	return &proxyError{
		msg:        msg,
		background: background,
	}
}

type proxyError struct {
	msg        string
	background error
}

func (err *proxyError) Error() string { return err.msg }

func (err *proxyError) Unwrap() error { return err.background }

View on GitHub (pinned to ec1a0befd2)

Solutions

  1. Tighten the WHERE clause so it selects at most one row (add the primary key or a LIMIT 1 if 'first match' semantics are intended).
  2. If more than one row is acceptable, use CollectOneRow (keeps the first, no error) or CollectRows.
  3. If you want exactly-one semantics, keep CollectExactlyOneRow and treat ErrTooManyRows as a data-integrity signal worth alerting on.
  4. Add/restore a UNIQUE constraint on the column(s) you assume identify a single row.

Example fix

// before
row, err := pgx.CollectExactlyOneRow(rows, pgx.RowToAddrOfStructByPos[User])
// err == pgx.ErrTooManyRows when duplicates exist

// after — first match is fine
row, err := pgx.CollectOneRow(rows, pgx.RowToAddrOfStructByPos[User])
Defensive patterns

Strategy: try-catch

Type guard

func isTooManyRows(err error) bool { return errors.Is(err, pgx.ErrTooManyRows) }

Try / catch

row, err := pgx.CollectExactlyOneRow(rows, fn)
switch {
case errors.Is(err, pgx.ErrTooManyRows):
    // duplicate data; alert or tighten the query
case errors.Is(err, pgx.ErrNoRows):
    // not found
default:
    return err
}

Prevention

When it happens

Trigger: Calling pgx.CollectExactlyOneRow(rows, fn) over a result set that yields 2+ rows (rows.Next() is true a second time at rows.go:516).

Common situations: A WHERE clause missing a uniqueness predicate; a join that fans out rows; a table whose unique constraint was dropped; querying a lookup table that gained a duplicate.


AI-assisted analysis of jackc/pgx@ec1a0befd2 (2026-08-04). Data as JSON: /data/errors/867af560d166d577.json. Report an issue: GitHub.