jaegertracing/jaeger · warning
failed to close rows: %w
Error message
failed to close rows: %w
What it means
When rows.Close() returns an error after finishing iteration in Reader.GetTraces, it is appended to the error list and yielded as part of the joined error. clickhouse-go's Close can fail when releasing the connection if the connection is already broken, so this typically accompanies (or masks) an earlier streaming failure.
Source
Thrown at internal/storage/v2/clickhouse/tracestore/reader.go:100
var errs []error
for rows.Next() {
span, scanErr := dbmodel.ScanRow(rows)
if scanErr != nil {
errs = append(errs, fmt.Errorf("failed to scan span row: %w", scanErr))
break
}
trace := dbmodel.FromRow(span)
if !yield([]ptrace.Traces{trace}, nil) {
_ = rows.Close()
return
}
}
if rowsErr := rows.Err(); rowsErr != nil {
errs = append(errs, fmt.Errorf("failed to read span rows: %w", rowsErr))
}
if closeErr := rows.Close(); closeErr != nil {
errs = append(errs, fmt.Errorf("failed to close rows: %w", closeErr))
}
if err := errors.Join(errs...); err != nil {
yield(nil, err)
return
}
}
}
}
func (r *Reader) GetServices(ctx context.Context) ([]string, error) {
rows, err := r.conn.Query(ctx, sql.SelectServices)
if err != nil {
return nil, fmt.Errorf("failed to query services: %w", err)
}
var (
services []string
errs []errorView on GitHub (pinned to 806f444784)
Solutions
- Inspect the joined error (errors.Join output) — the primary cause is usually the earlier wrapped error, not the close itself
- Fix the underlying connection/timeout problem; the close error is secondary
- Ensure queries honor context cancellation so teardown is clean
- Keep clickhouse-go driver versions current, as older versions had close-error edge cases
Example fix
// before cancelledCtx, cancel := context.WithCancel(ctx) cancel() rows, _ := conn.Query(cancelledCtx, q) // close fails on cancelled conn // after rows, _ := conn.Query(ctx, q) // let iteration finish before teardown
Defensive patterns
Strategy: try-catch
Validate before calling
// ensure clean cancellation semantics before querying ctx, cancel := context.WithTimeout(ctx, queryTimeout) defer cancel()
Try / catch
err := doQuery(ctx)
if err != nil {
// close errors are joined; inspect each with errors.Unwrap
for e := err; e != nil; e = errors.Unwrap(e) {
if strings.Contains(e.Error(), "failed to close rows") {
log.Printf("secondary close failure: %v", e)
break
}
}
} Prevention
- Don't cancel contexts while rows are still being iterated; drain or close explicitly
- Treat close errors as secondary — diagnose the primary wrapped error first
- Keep clickhouse-go updated for teardown edge cases
- Avoid server-side query kills by respecting resource limits
When it happens
Trigger: GetTraces iteration completes (or breaks on a scan error) and the subsequent rows.Close() call fails because the connection was reset, the query was cancelled, or the underlying TCP conn is in a bad state.
Common situations: Network instability between jaeger and ClickHouse; context cancellation racing with query teardown; server-side kills (max_execution_time, memory limits) leaving the connection unusable for release.
Related errors
- failed to query trace IDs: %w
- ttl must be a non-negative duration
- ttl must be a whole number of seconds
- default_search_depth must be a positive number
- max_search_depth must be a positive number
AI-assisted analysis of jaegertracing/jaeger@806f444784 (2026-09-01).
Data as JSON: /api/errors/ec24e30028acc938.
Report an issue: GitHub.