{"record":{"id":"3453b0100c6a9a5b","repo":"googleapis/mcp-toolbox","slug":"unable-to-retrieve-rows-column-name-w-3453b0","errorCode":null,"errorMessage":"unable to retrieve rows column name: %w","messagePattern":"unable to retrieve rows column name: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/sources/cloudsqlmysql/cloud_sql_mysql.go","lineNumber":143,"sourceCode":"func (s *Source) RetrieveSourceVersion(ctx context.Context) (string, error) {\n\tvar version string\n\tif err := s.MySQLPool().QueryRowContext(ctx, \"SELECT VERSION()\").Scan(&version); err != nil {\n\t\treturn \"\", err\n\t}\n\treturn version, nil\n}\n\nfunc (s *Source) RunSQL(ctx context.Context, statement string, params []any) (any, error) {\n\tstatement = sqlcommenter.PrependComment(ctx, statement, SourceType, s.SQLCommenter)\n\tresults, err := s.MySQLPool().QueryContext(ctx, statement, params...)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"unable to execute query: %w\", err)\n\t}\n\tdefer results.Close()\n\n\tcols, err := results.Columns()\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"unable to retrieve rows column name: %w\", err)\n\t}\n\n\t// create an array of values for each column, which can be re-used to scan each row\n\trawValues := make([]any, len(cols))\n\tvalues := make([]any, len(cols))\n\tfor i := range rawValues {\n\t\tvalues[i] = &rawValues[i]\n\t}\n\n\tcolTypes, err := results.ColumnTypes()\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"unable to get column types: %w\", err)\n\t}\n\n\tout := []any{}\n\tfor results.Next() {\n\t\terr := results.Scan(values...)\n\t\tif err != nil {","sourceCodeStart":125,"sourceCodeEnd":161,"githubUrl":"https://github.com/googleapis/mcp-toolbox/blob/8cc6e09de2ad7b8bffc77751799585a1401a48eb/internal/sources/cloudsqlmysql/cloud_sql_mysql.go#L125-L161","documentation":"This error is thrown when results.Columns() fails after a successful QueryContext in RunSQL. Columns() reads the result set metadata from the MySQL driver; failing here usually means the connection died between query submission and metadata retrieval, or the driver returned an unexpected protocol state.","triggerScenarios":"Calling RunSQL when the MySQL connection is dropped between QueryContext and Columns() (network interruption, server wait_timeout kill, instance restart), or a driver-level protocol error while fetching result metadata.","commonSituations":"Long-idle pooled connections killed by the server's wait_timeout, Cloud SQL instance restart/failover mid-request, transient network blips between the app and the instance, very large result metadata over an unstable link.","solutions":["Retry the query — it is usually transient (the next call gets a fresh pooled connection).","Configure the pool with connection lifetimes (SetConnMaxLifetime below server wait_timeout) and enable driver keepalives to avoid stale connections.","Check Cloud SQL logs for instance restarts or connection kills during the request window.","Verify network stability (VPC connector, NAT, proxy) between the runtime and the instance.","Update the go-sql-driver/mysql dependency if errors persist across driver versions."],"exampleFix":"// before\npool, err := sql.Open(\"mysql\", dsn)\n// after\npool.SetConnMaxLifetime(5 * time.Minute)\npool.SetConnMaxIdleTime(2 * time.Minute)","handlingStrategy":"retry","validationCode":"func ensureHealthyPool(db *sql.DB) error {\n    ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)\n    defer cancel()\n    if err := db.PingContext(ctx); err != nil {\n        return fmt.Errorf(\"pool unhealthy before query: %w\", err)\n    }\n    return nil\n}","typeGuard":"func isTransientConnectionLoss(err error) bool {\n    msg := err.Error()\n    return strings.Contains(msg, \"broken pipe\") ||\n        strings.Contains(msg, \"connection refused\") ||\n        strings.Contains(msg, \"bad connection\") ||\n        strings.Contains(msg, \"driver: bad connection\") ||\n        errors.Is(err, io.EOF)\n}","tryCatchPattern":"var result any\nvar err error\nfor attempt := 0; attempt < 2; attempt++ {\n    result, err = src.RunSQL(ctx, statement, params)\n    if err == nil {\n        break\n    }\n    if !isTransientConnectionLoss(err) || !strings.Contains(err.Error(), \"column name\") {\n        break // not retryable\n    }\n    time.Sleep(200 * time.Millisecond) // fresh pooled connection\n}","preventionTips":["Set pool.SetConnMaxLifetime below the MySQL server's wait_timeout so idle connections are retired before the server kills them.","Call PingContext (or set connection validation) before reusing long-idle pools.","Monitor Cloud SQL for restarts/failovers and add retry logic in callers of RunSQL.","Keep the go-sql-driver/mysql driver up to date.","Avoid very long-running queries that hold result sets across network-sensitive windows."],"tags":["mysql","resultset","connection-drop","cloudsql"],"backgroundTag":"stale-database-connection","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"}