risingwavelabs/risingwave · error · ConnectorError
Failed to get MySQL version
Error message
Failed to get MySQL version
What it means
get_mysql_version queries `SELECT VERSION()` and parses the string; if the query succeeds but no parseable `x.y...` version token is found, it returns this error. The reader needs the version (and MariaDB detection) for capability checks like mysql_native_password vs. caching_sha2 behavior.
Solutions
- Check what `SELECT VERSION()` returns on the target server and confirm it starts with a numeric version like '8.0.32'.
- Bypass or reconfigure proxies that rewrite the VERSION string.
- Upgrade the connector to handle the fork's version format, or pin a standard MySQL/MariaDB version for CDC.
- Verify the CDC user has permission to execute SELECT on the server (VERSION() is generally unrestricted, but proxies may block it).
Example fix
// before (proxy returns "mysql-advanced 8")
// after: fix VERSION() output or make parsing lenient
let version_str = /* row value */;
let Some((major, rest)) = version_str.split_once('.') else {
return Err(anyhow!("Failed to get MySQL version: '{}'".to_string(), version_str).into());
}; Defensive patterns
Strategy: try-catch
Validate before calling
// Sanity-check the target server before connecting CDC:
let version: String = sqlx::query_scalar("SELECT VERSION()").fetch_one(&conn).await?;
if !version.chars().next().map_or(false, |c| c.is_ascii_digit()) {
return Err(format!("server VERSION() is non-standard: {}", version));
} Type guard
fn is_parsable_mysql_version(v: &str) -> bool {
let mut parts = v.split('.');
parts.next().map_or(false, |m| m.chars().all(|c| c.is_ascii_digit()) && !m.is_empty())
&& parts.next().map_or(false, |m| m.chars().all(|c| c.is_ascii_digit()) && !m.is_empty())
} Try / catch
match connect_mysql_external_table(cfg).await {
Err(e) if e.to_string().contains("Failed to get MySQL version") => {
eprintln!("Check `SELECT VERSION()` output on the server/proxy: it must start with 'major.minor'");
Err(e)
}
other => other,
} Prevention
- Run `SELECT VERSION()` on the target before CDC setup and verify it starts with a numeric version.
- Avoid proxies/forks that rewrite VERSION().
- Test CDC connectivity against the exact server version in staging.
- Retry with a direct connection if a proxy is suspected.
When it happens
Trigger: MySqlExternalTableReader::connect runs get_mysql_version and the server returns a VERSION() string that does not match the expected `major.minor[.patch]` prefix pattern, or the result set is empty.
Common situations: Pointing CDC at MySQL-compatible services (ProxySQL in front, Vitess, TiDB, unusual forks) that return nonstandard VERSION() strings; permission issues yielding empty results.
Related errors
- BIT( ) type not supported
- failed to deserialize MySQL value into rust value
- failed to deserialize MySQL value into rw value
- failed to parse mysql default value expression, only…
- failed to parse mysql timestamp value
AI-assisted analysis of risingwavelabs/risingwave@6469eb736d (2026-09-11).
Data as JSON: /api/errors/a8e2ef30a76204cf.
Report an issue: GitHub.
Appendix: source
Thrown at src/connector/src/source/cdc/external/mysql.rs:545
/// Get MySQL version from the connection
async fn get_mysql_version(pool: &mysql_async::Pool) -> ConnectorResult<(u8, u8, bool)> {
let mut conn = pool.get_conn().await?;
let result: Option<String> = conn.query_first("SELECT VERSION()").await?;
if let Some(version_str) = result {
let parts: Vec<&str> = version_str.split('.').collect();
if parts.len() >= 2 {
let major_version = parts[0]
.parse::<u8>()
.context("Failed to parse major version")?;
let minor_version = parts[1]
.parse::<u8>()
.context("Failed to parse minor version")?;
let is_mariadb = version_str.to_lowercase().contains("mariadb");
return Ok((major_version, minor_version, is_mariadb));
}
}
Err(anyhow!("Failed to get MySQL version").into())
}
/// Check if MySQL version is 8.4 or later
fn is_mysql_8_4_or_later(&self) -> bool {
let (major, minor) = self.mysql_version;
major > 8 || (major == 8 && minor >= 4)
}
pub async fn new(
config: ExternalTableConfig,
rw_schema: Schema,
pk_indices: Vec<usize>,
) -> ConnectorResult<Self> {
let database = config.database.clone();
let table = config.table.clone();
let pool = build_mysql_connection_pool(
&config.host,
config.port.parse::<u16>().unwrap(),View on GitHub (pinned to 6469eb736d)