vitessio/vitess · error

can't set charset for statement %d in transaction %v: %v

Error message

can't set charset for statement %d in transaction %v: %v

What it means

Thrown by processTransaction when mysql.SetCharset fails while trying to switch the target connection's charset so a statement replays with the same charset it was logged with. Without this, statement text could be misinterpreted, so the transaction is aborted rather than replayed with wrong encoding.

Source

Thrown at go/vt/binlog/binlogplayer/binlog_player.go:466

		// Make sure the statement is replayed in the proper charset.
		if dbClient, ok := blp.dbClient.(*dbClientImpl); ok {
			var stmtCharset *binlogdatapb.Charset
			if stmt.Charset != nil {
				stmtCharset = stmt.Charset
			} else {
				// Streamer sends a nil Charset for statements that use the
				// charset we specified in the request.
				stmtCharset = blp.defaultCharset
			}
			if !proto.Equal(blp.currentCharset, stmtCharset) {
				// In regular MySQL replication, the charset is silently adjusted as
				// needed during event playback. Here we also adjust so that playback
				// proceeds, but in Vitess-land this usually means a misconfigured
				// server or a misbehaving client, so we spam the logs with warnings.
				log.Warn(fmt.Sprintf("BinlogPlayer changing charset from %v to %v for statement %d in transaction %v", blp.currentCharset, stmtCharset, i, tx))
				err = mysql.SetCharset(dbClient.dbConn, stmtCharset)
				if err != nil {
					return false, fmt.Errorf("can't set charset for statement %d in transaction %v: %v", i, tx, err)
				}
				blp.currentCharset = stmtCharset
			}
		}
		if _, err = blp.exec(string(stmt.Sql)); err == nil {
			continue
		}
		if sqlErr, ok := err.(*sqlerror.SQLError); ok && sqlErr.Number() == sqlerror.ERLockDeadlock {
			// Deadlock: ask for retry
			log.Info(fmt.Sprintf("Deadlock: %v", err))
			if err = blp.dbClient.Rollback(); err != nil {
				return false, err
			}
			return false, nil
		}
		_ = blp.dbClient.Rollback()
		return false, err
	}

View on GitHub (pinned to 01a25a7d17)

Solutions

  1. Verify the charset/collation exists on the target: `SHOW COLLATION LIKE '<from logged stmtCharset>'`.
  2. Upgrade the target MySQL/Vitess version to support the source's charsets, or convert source tables to charsets the target supports.
  3. Ensure target server character-set config includes the needed charsets (`character_set_server`, available charsets).
  4. If the connection is the issue, restart the stream for a fresh dbConn.

Example fix

// before: target lacks utf8mb4_0900_ai_ci
//   mysql.SetCharset(dbConn, stmtCharset) // ER_UNKNOWN_COLLATION
// after: upgrade target or align source
//   ALTER TABLE t CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci; -- on source
Defensive patterns

Strategy: validation

Validate before calling

// verify the source's charsets exist on the target before streaming
for _, cs := range sourceCharsets { // e.g. utf8mb4_0900_ai_ci
    var n int
    if err := targetDB.QueryRow(
        "SELECT COUNT(*) FROM information_schema.COLLATIONS WHERE COLLATION_NAME = ?", cs,
    ).Scan(&n); err != nil || n == 0 {
        return fmt.Errorf("target lacks collation %s: binlog replay would fail", cs)
    }
}

Prevention

When it happens

Trigger: mysql.SetCharset(dbClient.dbConn, stmtCharset) errors: the requested charset/collation doesn't exist on the target MySQL version, the connection is broken, or the source logged a charset the target server was not compiled/configured with.

Common situations: Replicating from MySQL 8 (utf8mb4 variants, newer collations like utf8mb4_0900_ai_ci) to an older target that lacks them; source and target MySQL versions diverged; connection dropped mid-transaction.

Related errors


AI-assisted analysis of vitessio/vitess@01a25a7d17 (2026-09-01). Data as JSON: /api/errors/7030f1264d0212c7. Report an issue: GitHub.