`normalizeTimeoutError` is overwriting dial error
Describe the bug I'm using a proxying driver here for Google CloudSQL. I'm trying to handle some errors in a way that provides a clear reason to the user.
When it attempts to dial here: https://github.com/jackc/pgx/blob/61d3c965ad442cc14d6b0e39e0ab3821f3684c03/pgconn/pgconn.go#L320
I get a DialError that wraps an net.OpError that wraps a poll.DeadlineExceededError: i/o timeout. And that error has useful information about what is going on behind the scenes. It looks like this:
However, this is passed to newPerDialConnectError and then normalizeTimeoutError. And eventually hands here:
https://github.com/jackc/pgx/blob/61d3c965ad442cc14d6b0e39e0ab3821f3684c03/pgconn/errors.go#L145
Which drops the useful error and loses all useful information.
Is there a way to wrap this error so that useful information isn't lost downstream?
Interestingly, its not hitting my context timeout - in fact my context is dropped and that at least partially seems to be due to how database/sql drops the context here:
https://github.com/golang/go/blob/ac803b5949f6dbc5bfa559afe506d35f9e1b3195/src/database/sql/sql.go#L809
And then pgx generates a new context here:
https://github.com/jackc/pgx/blob/61d3c965ad442cc14d6b0e39e0ab3821f3684c03/stdlib/sql.go#L325
So ultimately
To Reproduce I don't have a full, runnable example, but this is the entrypoint back to pgx
db, err := sql.Open(sqlDriverPostgresIam, dsn)
if err != nil {
return fmt.Errorf("failed to open database connection: %w", err)
}
var result int
err = db.QueryRowContext(ctx, "SELECT 1").Scan(&result)
Expected behavior I'd like to return information from the upstream error, which has information on the IP that it cannot connect to, etc.
Actual behavior I'm getting a less useful and even confusing error about failing to connect to localhost, which is just the proxy part of the driver.
Version
- Go:
go version go1.24.7 darwin/arm64 - PostgreSQL: 17
- pgx:
v5.7.6
Additional context Driver: https://github.com/GoogleCloudPlatform/cloud-sql-go-connector/blob/main/postgres/pgxv5/postgres.go
I think this is actually working as designed. The underlying poll.DeadlineExceededError: i/o timeout is an implementation detail. pgx uses SetDeadline to interrupt connections when a context is canceled. normalizeTimeoutError so the caller gets a context error rather than whatever underlying error bubbled up from the interrupted net operation.
My guess is that 60 second failsafe context is being triggered.