benbjohnson/litestream · error

commit transaction: %w

Error message

commit transaction: %w

What it means

populateTable wraps a failed tx.Commit in this error. After inserting all rows of a batch the transaction is committed to make the batch durable; the WAL then grows and litestream can pick up the change. A commit failure means the entire batch is rolled back and population aborts (ctx cancellation is checked after each successful commit, not here).

Source

Thrown at cmd/litestream-test/populate.go:210

		}

		for j := i; j < batchEnd; j++ {
			cryptorand.Read(data)
			textField := fmt.Sprintf("row_%d_%d", i, j)
			intField := rand.Int63()
			floatField := rand.Float64() * 1000
			timestamp := time.Now().Unix()

			if _, err := stmt.Exec(data, textField, intField, floatField, timestamp); err != nil {
				stmt.Close()
				tx.Rollback()
				return fmt.Errorf("insert row: %w", err)
			}
		}

		stmt.Close()
		if err := tx.Commit(); err != nil {
			return fmt.Errorf("commit transaction: %w", err)
		}

		select {
		case <-ctx.Done():
			return ctx.Err()
		default:
		}
	}

	return nil
}

func (c *PopulateCommand) Usage() {
	fmt.Fprintln(c.Main.Stdout, `
Populate a SQLite database to a target size for testing.

Usage:

View on GitHub (pinned to 4ed7a308f6)

Solutions

  1. Check free disk space including room for WAL growth; reduce BatchSize if transactions are too large.
  2. Inspect the wrapped SQLite error for SQLITE_FULL/SQLITE_IOERR.
  3. Verify the filesystem is writable and quotas are not exceeded.
  4. Ensure only one writer (this tool) touches the database during population.

Example fix

// before
if err := tx.Commit(); err != nil {
    return fmt.Errorf("commit transaction: %w", err)
}
// after
if err := tx.Commit(); err != nil {
    return fmt.Errorf("commit transaction (batch %d-%d): %w", i, batchEnd, err) // include batch range for diagnosis
}
Defensive patterns

Strategy: validation

Validate before calling

// ensure free space for WAL before large batch commits
var st syscall.Statfs_t
if err := syscall.Statfs(filepath.Dir(c.DB), &st); err == nil {
    avail := int64(st.Bavail) * int64(st.Bsize)
    if avail < int64(c.BatchSize*c.RowSize)*4 {
        return fmt.Errorf("low disk for WAL: %d bytes available", avail)
    }
}

Try / catch

if err := tx.Commit(); err != nil {
    if strings.Contains(err.Error(), "SQLITE_FULL") || strings.Contains(err.Error(), "SQLITE_IOERR") {
        // operational failure: free space / check fs, then retry run
        return fmt.Errorf("commit transaction (operational): %w", err)
    }
    return fmt.Errorf("commit transaction: %w", err)
}

Prevention

When it happens

Trigger: tx.Commit() fails: disk full while flushing WAL frames, I/O error on the WAL or database file, the database was locked/invalidated by another connection during the transaction, or driver connection loss.

Common situations: Large BatchSize making big transactions that exhaust free space when written to the WAL; filesystem errors (quota, read-only remount); a concurrent process forcing WAL checkpoint failures.

Related errors


AI-assisted analysis of benbjohnson/litestream@4ed7a308f6 (2026-09-06). Data as JSON: /api/errors/523fc879589bf53e. Report an issue: GitHub.