gastownhall/beads · error

db: SetConfig %s: %w

Error message

db: SetConfig %s: %w

What it means

This error wraps failure of the REPLACE INTO config statement inside SetConfig. SetConfig writes a config key/value and then re-syncs the normalized lookup tables (custom_types, custom_statuses); this wrapper specifically covers the initial row write failing. The SQL repo intentionally mirrors DoltStore.SetConfig semantics so both backends behave identically.

Source

Thrown at internal/storage/domain/db/config.go:83

func (r *configSQLRepositoryImpl) GetConfig(ctx context.Context, key string) (string, error) {
	var value string
	err := r.runner.QueryRowContext(ctx, "SELECT value FROM config WHERE `key` = ?", key).Scan(&value)
	if errors.Is(err, sql.ErrNoRows) {
		return "", nil
	}
	if err != nil {
		return "", fmt.Errorf("db: GetConfig %s: %w", key, err)
	}
	return value, nil
}

func (r *configSQLRepositoryImpl) SetConfig(ctx context.Context, key, value string) error {
	if key == "issue_prefix" {
		value = strings.TrimSuffix(value, "-")
	}
	if _, err := r.runner.ExecContext(ctx, "REPLACE INTO config (`key`, value) VALUES (?, ?)", key, value); err != nil {
		return fmt.Errorf("db: SetConfig %s: %w", key, err)
	}
	// Re-sync the normalized lookup table a value backs, mirroring
	// DoltStore.SetConfig. Reads are TABLE-FIRST — GetCustomTypes above
	// consults custom_types and falls back to the string only when the table is
	// empty, and GetCustomStatuses reads custom_statuses outright — so a write
	// that updated only the string left the table holding the previous set,
	// forever: `bd config set types.custom` on a proxied deployment reported
	// success and `bd create -t <the new type>` kept answering "invalid issue
	// type", with doctor re-verifying against the string and reporting all-OK.
	//
	// The caller supplies a transactional runner, so the row and its projection
	// commit together or neither does.
	if _, err := issueops.SyncConfigTables(ctx, r.runner, key, value); err != nil {
		return fmt.Errorf("db: SetConfig %s: %w", key, err)
	}
	return nil
}

View on GitHub (pinned to 71377f2769)

Solutions

  1. Run schema setup/migration (e.g. `bd doctor`) to ensure the config table exists
  2. Retry after confirming no other process holds the database lock
  3. Check disk space and write permissions on the database file
  4. Inspect the wrapped cause and address the specific driver error
  5. Restore from backup if the database is corrupted
Defensive patterns

Strategy: retry

Validate before calling

if err := ensureSchemaMigrated(ctx); err != nil {
	return fmt.Errorf("config table unavailable: %w", err)
}
if locked := dbLockedByOtherProcess(); locked {
	return fmt.Errorf("database locked by another bd process")
}

Try / catch

err := retry.Do(3, retry.Backoff(time.Second), func() error {
	return repo.SetConfig(ctx, key, value)
})
if err != nil {
	return fmt.Errorf("could not set config %q after retries: %w", key, err)
}

Prevention

When it happens

Trigger: Calling SetConfig(ctx, key, value) when the REPLACE INTO config (`key`, value) ExecContext fails: missing config table, database lock, connection loss, or driver constraint/IO error.

Common situations: Running `bd config set` or prefix setup against a stale-schema database; concurrent bd processes contending for the DB lock; disk full while writing; an old binary opening a migrated database.

Related errors


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