googleapis/mcp-toolbox · error
error iterating rows: %w
Error message
error iterating rows: %w
What it means
Wraps rows.Err() after row iteration in RunSQL — the deferred error that database/sql accumulates if the result stream broke mid-iteration (network drop, server killed the connection, context cancelled). This is distinct from scan failures; it means rows were being fetched fine and then the stream failed.
Source
Thrown at internal/sources/firebird/firebird.go:142
err = rows.Scan(scanArgs...)
if err != nil {
return nil, fmt.Errorf("unable to parse row: %w", err)
}
vMap := make(map[string]any)
for i, col := range cols {
if b, ok := values[i].([]byte); ok {
vMap[col] = string(b)
} else {
vMap[col] = values[i]
}
}
out = append(out, vMap)
}
if err := rows.Err(); err != nil {
return nil, fmt.Errorf("error iterating rows: %w", err)
}
// In most cases, DML/DDL statements like INSERT, UPDATE, CREATE, etc. might return no rows
// However, it is also possible that this was a query that was expected to return rows
// but returned none, a case that we cannot distinguish here.
return out, nil
}
func initFirebirdConnectionPool(ctx context.Context, tracer trace.Tracer, name, host, port, user, pass, dbname string) (*sql.DB, error) {
_, span := sources.InitConnectionSpan(ctx, tracer, SourceType, name)
defer span.End()
// urlExample := "user:password@host:port/path/to/database.fdb"
dsn := fmt.Sprintf("%s:%s@%s:%s/%s", user, pass, host, port, dbname)
db, err := sql.Open("firebirdsql", dsn)
if err != nil {
return nil, fmt.Errorf("unable to create connection pool: %w", err)View on GitHub (pinned to 8cc6e09de2)
Solutions
- Paginate large results with FIRST/SKIP or a keyset (WHERE id > ?) instead of one huge result set
- Ensure ctx timeout exceeds expected fetch duration, or use context.WithoutCancel for fetch-only work
- Enable TCP keepalives / adjust firewall idle timeouts
- Retry transient failures at the caller for read-only queries
Example fix
// before
if err := rows.Err(); err != nil {
return nil, fmt.Errorf("error iterating rows: %w", err)
}
// after
if err := rows.Err(); err != nil {
if ctx.Err() != nil {
return nil, fmt.Errorf("error iterating rows: %w", ctx.Err())
}
return nil, fmt.Errorf("error iterating rows: %w", err)
} Defensive patterns
Strategy: retry
Validate before calling
// Bound result size before fetching
if !strings.Contains(strings.ToUpper(statement), "FIRST") {
return errors.New("unbounded query: add FIRST/SKIP pagination for large result sets")
} Type guard
func isTransientStreamErr(err error, ctx context.Context) bool {
return ctx.Err() == nil && (errors.Is(err, io.ErrUnexpectedEOF) || errors.Is(err, syscall.ECONNRESET))
} Try / catch
if err := rows.Err(); err != nil {
if isTransientStreamErr(err, ctx) {
// safe to retry the whole read-only query with backoff
}
return nil, fmt.Errorf("error iterating rows: %w", err)
} Prevention
- Paginate with FIRST/SKIP or keyset predicates for big tables
- Align ctx deadlines with worst-case fetch time
- Enable TCP keepalive and raise firewall idle timeouts
- Always check rows.Err() after the loop — never assume iteration succeeded
When it happens
Trigger: RunSQL iterating a large result set when the TCP connection drops, the ctx is cancelled, Firebird server restarts, or a sweep/garbage-collection issue kills the cursor server-side.
Common situations: Long-running queries over slow/unstable networks; context timeout shorter than full result fetch; firewalls killing idle connections mid-stream; very large result sets exceeding server limits.
Related errors
- errors encountered during row iteration: %w
- errors encountered during row iteration: %w
- toolbox failed to start listener: %w
- toolbox failed to start listener: %w
- failed to discover OIDC config: %w
AI-assisted analysis of googleapis/mcp-toolbox@8cc6e09de2 (2026-09-05).
Data as JSON: /api/errors/6f9b6ea413075c85.
Report an issue: GitHub.