benbjohnson/litestream · error

populate table

Error message

populate table %s: %w

What it means

populateDatabase wraps a failure from populateTable for a given test table in this error. After creating tables, each table is filled with rowsPerTable rows in batches; any failure inside populateTable (transaction begin, prepare, insert, commit) is surfaced here with the table name. The remaining tables are not populated and the command exits.

Solutions

  1. Inspect the wrapped cause (the insert/commit/begin error) to find the root failure.
  2. Check free disk space; SQLite writes fail with SQLITE_FULL when out of space.
  3. Reduce target size or RowSize to fit available disk.
  4. Ensure no competing process holds a write lock during population.

Example fix

// before
err := c.populateTable(ctx, db, tableName, rowsPerTable) // opaque failure
// after
err := c.populateTable(ctx, db, tableName, rowsPerTable)
if err != nil {
    slog.Error("populate failed", "table", tableName, "cause", errors.Unwrap(err))
}
Defensive patterns

Strategy: retry

Validate before calling

// preflight: disk space and lock check before population
if fi, err := os.Stat(filepath.Dir(c.DB)); err != nil {
    return fmt.Errorf("db dir unavailable: %w", err)
}
// verify no other writer holds the db
if conn, err := os.OpenFile(c.DB, os.O_RDWR, 0); err != nil {
    return fmt.Errorf("db not writable: %w", err)
} else {
    conn.Close()
}

Try / catch

if err := c.populateTable(ctx, db, tableName, rowsPerTable); err != nil {
    if errors.Is(err, sqlite.ErrBusy) || strings.Contains(err.Error(), "SQLITE_BUSY") {
        time.Sleep(time.Second)
        return c.populateTable(ctx, db, tableName, rowsPerTable) // retry once
    }
    return fmt.Errorf("populate table %s: %w", tableName, err)
}

Prevention

When it happens

Trigger: populateTable returns an error for table test_table_%d: the batch insert transaction fails to begin, the INSERT statement fails to prepare or execute, or the commit fails. The original cause is available via errors.Unwrap.

Common situations: Disk filling mid-population on a large target size; context cancellation surfacing through a failing database call; locked database from a concurrent reader (e.g. litestream or a monitoring query).

Related errors


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

Appendix: source

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

	totalRows := int(targetBytes / int64(c.RowSize))
	rowsPerTable := totalRows / c.TableCount
	if rowsPerTable == 0 {
		rowsPerTable = 1
	}

	slog.Info("Populating database",
		"target_bytes", targetBytes,
		"total_rows", totalRows,
		"rows_per_table", rowsPerTable,
	)

	startTime := time.Now()
	for tableIdx := 0; tableIdx < c.TableCount; tableIdx++ {
		tableName := fmt.Sprintf("test_table_%d", tableIdx)

		if err := c.populateTable(ctx, db, tableName, rowsPerTable); err != nil {
			return fmt.Errorf("populate table %s: %w", tableName, err)
		}

		currentSize, _ := getDatabaseSize(c.DB)
		progress := float64(currentSize) / float64(targetBytes) * 100
		slog.Info("Progress",
			"table", tableName,
			"current_size_mb", currentSize/1024/1024,
			"progress_percent", fmt.Sprintf("%.1f", progress),
		)

		if currentSize >= targetBytes {
			break
		}
	}

	duration := time.Since(startTime)
	finalSize, _ := getDatabaseSize(c.DB)

View on GitHub (pinned to 4ed7a308f6)