gastownhall/beads · error

check pending changes before commit: %w

Error message

check pending changes before commit: %w

What it means

StageAndCommit first calls issueops.HasPendingChanges to decide whether anything is worth committing; failures there are wrapped as 'check pending changes before commit: %w'. This is a pre-flight dolt_status query, so the error usually reflects a connection or query problem, not a commit problem. The commit is intentionally skipped when nothing is pending.

Source

Thrown at internal/storage/versioncontrolops/commit.go:68

	}

	// dirtyTables tracks tables touched by a write statement, but a statement
	// can succeed without changing any rows (e.g. an idempotent
	// "INSERT ... ON DUPLICATE KEY UPDATE value = VALUES(value)" re-writing the
	// same value, an INSERT IGNORE that hit a duplicate, or an UPDATE whose WHERE
	// matched nothing). Staging + committing in that case is a no-op that Dolt
	// rejects with a "nothing to commit" warning logged server-side on every call
	// — at high-frequency callers (config/metadata heartbeats, reconcile counters,
	// idempotent label/dependency writes) this floods the Dolt log.
	//
	// Cheap fast-path: if NOTHING is pending in the whole working set (excluding
	// dolt-ignored tables, which cannot be staged), skip without touching Dolt's
	// staging machinery. Note: callers like Update/Close also write an events row,
	// so a zero-rows main-table write can still be a real change — dolt_status
	// captures that correctly where a rows-affected check would not.
	pending, err := issueops.HasPendingChanges(ctx, conn)
	if err != nil {
		return fmt.Errorf("check pending changes before commit: %w", err)
	}
	if !pending {
		return nil
	}

	for table := range dirtyTables {
		if _, err := conn.ExecContext(ctx, "CALL DOLT_ADD(?)", table); err != nil {
			return fmt.Errorf("dolt add %s: %w", table, err)
		}
	}

	// Precise guard: HasPendingChanges above is global, but we only DOLT_ADD the
	// dirty-tracked tables. When those specific tables turn out clean (idempotent
	// no-op) while some UNRELATED table is concurrently dirty, the fast-path does
	// not fire yet staging stages nothing — so DOLT_COMMIT('-m') would still emit
	// the "nothing to commit" warning. Check the STAGED set (exactly what '-m'
	// will commit) and skip the empty commit.
	staged, err := issueops.HasStagedChanges(ctx, conn)

View on GitHub (pinned to 71377f2769)

Solutions

  1. Inspect the wrapped driver error and reconnect/reopen the Dolt connection if it's a connection failure
  2. Retry StageAndCommit after connectivity is restored — it is safe to re-run
  3. Verify dolt_status works on the target Dolt version
  4. Check whether a transaction/lock conflict is blocking the dolt_status read and release the conflicting session

Example fix

// before
err := versioncontrolops.StageAndCommit(ctx, db, tables, msg, author)
// after
if err := db.PingContext(ctx); err != nil {
    db = reconnect(ctx)
}
err := versioncontrolops.StageAndCommit(ctx, db, tables, msg, author)
Defensive patterns

Strategy: retry

Validate before calling

if err := conn.PingContext(ctx); err != nil {
    return fmt.Errorf("connection unhealthy before commit: %w", err)
}

Try / catch

err := versioncontrolops.StageAndCommit(ctx, conn, tables, msg, author)
if err != nil && strings.Contains(err.Error(), "check pending changes before commit") {
    conn = reconnect(ctx)
    err = versioncontrolops.StageAndCommit(ctx, conn, tables, msg, author)
}

Prevention

When it happens

Trigger: Calling StageAndCommit when the HasPendingChanges query (dolt_status scan) fails — dead connection, Dolt server unavailable, or schema/version change affecting dolt_status output.

Common situations: Server restarted between writes and commit; connection pool returned a broken session; embedded Dolt process crashed; querying dolt_status on a database where that procedure is unavailable.

Related errors


AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30). Data as JSON: /api/errors/87ce70b16fe355aa. Report an issue: GitHub.