cloudreve/cloudreve · error

failed to start transaction: %w

Error message

failed to start transaction: %w

What it means

Returned when m.v4client.Tx(context.Background()) fails to open the batch transaction on the v4 target (application/migrator/user.go:44-47). ent returns a nil *Tx with the error; as with the share migrator, the following _ = tx.Rollback() runs on that nil Tx and ent's Tx.Rollback dereferences tx.config (ent/tx.go:148-149), so this path usually surfaces as a nil-pointer panic rather than the wrapped 'failed to start transaction' error.

Source

Thrown at application/migrator/user.go:45

	for {
		m.l.Info("Migrating users with offset %d", offset)
		var users []model.User
		if err := model.DB.Limit(batchSize).Offset(offset).Find(&users).Error; err != nil {
			return fmt.Errorf("failed to list v3 users: %w", err)
		}

		if len(users) == 0 {
			if m.dep.ConfigProvider().Database().Type == conf.PostgresDB {
				m.l.Info("Resetting user ID sequence for postgres...")
				m.v4client.User.ExecContext(ctx, "SELECT SETVAL('users_id_seq',  (SELECT MAX(id) FROM users))")
			}
			break
		}

		tx, err := m.v4client.Tx(context.Background())
		if err != nil {
			_ = tx.Rollback()
			return fmt.Errorf("failed to start transaction: %w", err)
		}

		for _, u := range users {
			userStatus := user.StatusActive
			switch u.Status {
			case model.Active:
				userStatus = user.StatusActive
			case model.NotActivicated:
				userStatus = user.StatusInactive
			case model.Baned:
				userStatus = user.StatusManualBanned
			case model.OveruseBaned:
				userStatus = user.StatusSysBanned
			}

			setting := &types.UserSetting{
				VersionRetention:    true,
				VersionRetentionMax: 10,

View on GitHub (pinned to 20c95ad73f)

Solutions

  1. If you got a panic instead of this error string, the nil tx.Rollback() is the cause - resolve the underlying connection failure.
  2. Confirm v4 connectivity/credentials from the migration host.
  3. Free up connection slots (stop the v4 instance during migration) or raise max_connections.
  4. Patch out the _ = tx.Rollback() line so future failures return the real error.

Example fix

// before
tx, err := m.v4client.Tx(context.Background())
if err != nil {
	_ = tx.Rollback() // tx is nil -> panic
	return fmt.Errorf("failed to start transaction: %w", err)
}

// after
tx, err := m.v4client.Tx(context.Background())
if err != nil {
	return fmt.Errorf("failed to start transaction: %w", err)
}
Defensive patterns

Strategy: validation

Validate before calling

// Round-trip test before the loop
tx, err := v4client.Tx(context.Background())
if err != nil {
	return fmt.Errorf("v4 DB cannot open transaction: %w", err)
}
_ = tx.Rollback()

Type guard

if tx == nil && err != nil {
	// ent failed to create the Tx; touching tx.Rollback() panics
	return err
}

Try / catch

tx, err := m.v4client.Tx(context.Background())
if err != nil {
	// no Rollback call: tx is nil here
	return fmt.Errorf("failed to start transaction: %w", err)
}

Prevention

When it happens

Trigger: v4 DB down/unreachable; too many connections; missing transaction privilege; connect timeout from an exhausted pool; context canceled by an upstream shutdown.

Common situations: Starting user migration before the v4 database is ready; DB connection limits consumed by the app plus migration; wrong v4 DSN passed to the migrator; Kubernetes pod DNS not resolving the DB service.

Related errors


AI-assisted analysis of cloudreve/cloudreve@20c95ad73f (2026-08-16). Data as JSON: /api/errors/30287455e6530af9. Report an issue: GitHub.