vitessio/vitess · error

mismatched ShardSession count: originally %d, now %d

Error message

mismatched ShardSession count: originally %d, now %d

What it means

The vitess database/sql driver stores shard sessions returned in session tokens. When a session token passed in contains MORE ShardSessions than the original count recorded when the session was first created, something replaced or regenerated the session token mid-transaction; the driver refuses to commit state that grew beyond what it created, returning this mismatch error.

Source

Thrown at go/vt/vitessdriver/driver.go:360

	}

	// this is designed to be run after all new work has been done in the tx, similar to
	// where you would traditionally run a tx.Commit, to help prevent you from silently
	// losing transactional data.
	validationFunc := func() error {
		var sessionToken string
		sessionToken, err = SessionTokenFromTx(ctx, tx)
		if err != nil {
			return err
		}

		session, err = sessionTokenToSession(sessionToken)
		if err != nil {
			return err
		}

		if len(session.ShardSessions) > originalShardSessionCount {
			return fmt.Errorf("mismatched ShardSession count: originally %d, now %d",
				originalShardSessionCount, len(session.ShardSessions),
			)
		}

		return nil
	}

	return tx, validationFunc, nil
}

// SessionTokenFromTx serializes the sessionFromToken on the tx, which can be reconstituted
// into a *sql.Tx using DistributedTxFromSessionToken
func SessionTokenFromTx(ctx context.Context, tx *sql.Tx) (string, error) {
	var sessionToken string

	err := tx.QueryRowContext(ctx, "vt_session_token").Scan(&sessionToken)
	if err != nil {
		return "", err

View on GitHub (pinned to 01a25a7d17)

Solutions

  1. Ensure each transaction's session token is used only within that transaction — never reuse or share tokens across transactions or connections.
  2. Get a fresh session token by beginning a new transaction instead of re-committing an old token.
  3. If using the driver directly, verify the token passed to Commit originates from the same conn/session that called Begin.

Example fix

// before: token reused across transactions
token := tx1.SessionToken()
tx2.CommitWithToken(token) // mismatch
// after: commit the token of the same transaction
token := tx1.SessionToken()
tx1.CommitWithToken(token)
Defensive patterns

Strategy: validation

Validate before calling

token := tx.SessionToken()
sess, err := sessionTokenToSession(token)
if err != nil {
    return err
}
if len(sess.ShardSessions) != expectedSessionCount {
    return fmt.Errorf("token for wrong transaction: %d sessions", len(sess.ShardSessions))
}

Type guard

func isShardSessionCountMismatch(err error) bool {
    return err != nil && strings.Contains(err.Error(), "mismatched ShardSession count")
}

Try / catch

if err := conn.CommitWithToken(token); err != nil {
    if isShardSessionCountMismatch(err) {
        return fmt.Errorf("session token reused across transactions; start a new transaction: %w", err)
    }
    return err
}

Prevention

When it happens

Trigger: Calling Commit (via the driver's commit path in vitessdriver) with a session token whose ShardSessions slice length exceeds originalShardSessionCount — typically when a session token from a different transaction/lifetime is committed, or tokens are reused/shared across transactions incorrectly.

Common situations: Application code reusing a session token across transactions or copying it between connections; serialization/deserialization round-trips that merged session states; driver misuse where Begin/Commit is interleaved with tokens from another session.

Related errors


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