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
- 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).
- If more than one row is acceptable, use CollectOneRow (keeps the first, no error) or CollectRows.
- If you want exactly-one semantics, keep CollectExactlyOneRow and treat ErrTooManyRows as a data-integrity signal worth alerting on.
- 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
- Use CollectOneRow when duplicates are acceptable.
- Add LIMIT 1 or a uniqueness predicate to the query.
- Enforce a UNIQUE constraint on the column(s) you assume single-row.
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.