Skip to content

How to Fix Go Database/sql Rows Iteration Errors

DodaTech Updated 2026-06-24 2 min read

In this tutorial, you'll learn about How to Fix Go Database/sql Rows Iteration Errors. We cover key concepts, practical examples, and best practices.

Go database/sql errors like sql: Rows closed or sql: Rows Next iteration failed occur when rows are not properly iterated, the connection is closed mid-iteration, or rows.Close() is deferred incorrectly.

Quick Fix

Wrong

rows, err := db.Query("SELECT id, name FROM users")
if err != nil {
    log.Fatal(err)
}

for rows.Next() {
    var id int
    var name string
    rows.Scan(&id, &name)
    fmt.Println(id, name)
}
// Missing rows.Close() and rows.Err() check

If the loop breaks early (e.g., due to an error or break statement), the rows are not closed, leaking the database connection.

rows, err := db.Query("SELECT id, name FROM users")
if err != nil {
    log.Fatal(err)
}
defer rows.Close()

for rows.Next() {
    var id int
    var name string
    if err := rows.Scan(&id, &name); err != nil {
        log.Fatal(err)
    }
    fmt.Println(id, name)
}
if err := rows.Err(); err != nil {
    log.Fatal(err)
}
1 Alice
2 Bob

Fix when breaking early

rows, err := db.Query("SELECT id, name FROM users")
if err != nil {
    log.Fatal(err)
}
defer rows.Close()

for rows.Next() {
    var id int
    var name string
    rows.Scan(&id, &name)
    if name == "Alice" {
        break // defer closes rows
    }
}

Fix for multiple result sets

rows, err := db.Query("SELECT id FROM users; SELECT id FROM orders")
if err != nil {
    log.Fatal(err)
}
defer rows.Close()

for rows.Next() {
    var id int
    rows.Scan(&id)
    fmt.Println("User:", id)
}

if rows.NextResultSet() {
    for rows.Next() {
        var id int
        rows.Scan(&id)
        fmt.Println("Order:", id)
    }
}

Prevention

  • Always defer rows.Close() immediately after checking query error.
  • Always check rows.Err() after the iteration loop.
  • Use rows.Close() before deferred close for early termination when needed.
  • Handle rows.Scan errors inside the loop.
  • Use prepared statements for repeated queries.

DodaTech Tools

Doda Browser's Go database tracer shows query execution plans and row iteration counts. DodaZIP archives slow query logs. Durga Antivirus Pro detects SQL injection attempts through row iteration patterns.

Common Mistakes with sql rows iteration

  1. Using head and tail instead of pattern matching, causing runtime errors on empty lists
  2. Forgetting that lazy evaluation defers computation until the value is forced, causing space leaks with unevaluated thunks
  3. Using return to exit a function early instead of wrapping a pure value in the monad

These mistakes appear frequently in real-world GO code. DodaTech's contributors have identified these patterns through analysis of open-source projects and production systems.

Practice Exercise

Write a pure function that safely divides two integers using Maybe, then test it with edge cases like division by zero and negative numbers.

This exercise reinforces the concepts covered in this guide. Try implementing it before checking online solutions.

FAQ

Can I call `rows.Close()` multiple times safely?

Yes, rows.Close() is idempotent. Calling it multiple times is safe. However, the deferred call after an explicit close is harmless, not an error.

What happens if I do not call `rows.Close()`?

The database connection underlying the rows is not returned to the pool until the rows are garbage collected or explicitly closed. This can exhaust the connection pool.

Should I check `rows.Err()` after iteration?

Yes, always. rows.Err() catches errors that happened during iteration (e.g., network failures) that are not returned by individual rows.Next() or rows.Scan() calls.

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro