{"record":{"id":"6f9b6ea413075c85","repo":"googleapis/mcp-toolbox","slug":"error-iterating-rows-w","errorCode":null,"errorMessage":"error iterating rows: %w","messagePattern":"error iterating rows: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/sources/firebird/firebird.go","lineNumber":142,"sourceCode":"\n\t\terr = rows.Scan(scanArgs...)\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"unable to parse row: %w\", err)\n\t\t}\n\n\t\tvMap := make(map[string]any)\n\t\tfor i, col := range cols {\n\t\t\tif b, ok := values[i].([]byte); ok {\n\t\t\t\tvMap[col] = string(b)\n\t\t\t} else {\n\t\t\t\tvMap[col] = values[i]\n\t\t\t}\n\t\t}\n\t\tout = append(out, vMap)\n\t}\n\n\tif err := rows.Err(); err != nil {\n\t\treturn nil, fmt.Errorf(\"error iterating rows: %w\", err)\n\t}\n\n\t// In most cases, DML/DDL statements like INSERT, UPDATE, CREATE, etc. might return no rows\n\t// However, it is also possible that this was a query that was expected to return rows\n\t// but returned none, a case that we cannot distinguish here.\n\treturn out, nil\n}\n\nfunc initFirebirdConnectionPool(ctx context.Context, tracer trace.Tracer, name, host, port, user, pass, dbname string) (*sql.DB, error) {\n\t_, span := sources.InitConnectionSpan(ctx, tracer, SourceType, name)\n\tdefer span.End()\n\n\t// urlExample := \"user:password@host:port/path/to/database.fdb\"\n\tdsn := fmt.Sprintf(\"%s:%s@%s:%s/%s\", user, pass, host, port, dbname)\n\n\tdb, err := sql.Open(\"firebirdsql\", dsn)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"unable to create connection pool: %w\", err)","sourceCodeStart":124,"sourceCodeEnd":160,"githubUrl":"https://github.com/googleapis/mcp-toolbox/blob/8cc6e09de2ad7b8bffc77751799585a1401a48eb/internal/sources/firebird/firebird.go#L124-L160","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"// before\nif err := rows.Err(); err != nil {\n    return nil, fmt.Errorf(\"error iterating rows: %w\", err)\n}\n// after\nif err := rows.Err(); err != nil {\n    if ctx.Err() != nil {\n        return nil, fmt.Errorf(\"error iterating rows: %w\", ctx.Err())\n    }\n    return nil, fmt.Errorf(\"error iterating rows: %w\", err)\n}","handlingStrategy":"retry","validationCode":"// Bound result size before fetching\nif !strings.Contains(strings.ToUpper(statement), \"FIRST\") {\n    return errors.New(\"unbounded query: add FIRST/SKIP pagination for large result sets\")\n}","typeGuard":"func isTransientStreamErr(err error, ctx context.Context) bool {\n    return ctx.Err() == nil && (errors.Is(err, io.ErrUnexpectedEOF) || errors.Is(err, syscall.ECONNRESET))\n}","tryCatchPattern":"if err := rows.Err(); err != nil {\n    if isTransientStreamErr(err, ctx) {\n        // safe to retry the whole read-only query with backoff\n    }\n    return nil, fmt.Errorf(\"error iterating rows: %w\", err)\n}","preventionTips":["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"],"tags":["firebird","row-iteration","network","context-cancellation"],"backgroundTag":"connection-dropped-mid-query","analyzedSha":"8cc6e09de2ad7b8bffc77751799585a1401a48eb","analyzedAt":"2026-09-05T01:10:36.887Z","contentChangedAt":"2026-09-05T01:10:36.887Z","schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}