gastownhall/beads · error

db: SetLocalMetadata

Error message

db: SetLocalMetadata %s: %w

What it means

This error wraps the underlying SQL driver failure from an INSERT/REPLACE into the local_metadata table in the Dolt-backed storage layer. SetLocalMetadata writes a key/value row used for repo-local metadata; when the ExecContext call fails, the error is wrapped as "db: SetLocalMetadata <key>: <cause>" so callers know which metadata key could not be persisted. The root cause is always in the wrapped driver error (connection loss, schema problem, constraint, etc.).

Solutions

  1. Run the driver's schema/migration path (e.g. `bd doctor` or re-open the repo) to ensure the local_metadata table exists
  2. Inspect the wrapped cause (the %w chain) to identify whether it is I/O, locking, or schema related and fix accordingly
  3. Check disk space and file permissions on the database directory
  4. Ensure no other process holds a conflicting lock on the Dolt database, then retry
  5. If the database is corrupt, restore from backup or re-init the repo

Example fix

// before
if _, err := db.Exec("REPLACE INTO local_metadata (`key`, value) VALUES (?, ?)", k, v); err != nil {
	return fmt.Errorf("set failed")
}
// after
if _, err := db.Exec("REPLACE INTO local_metadata (`key`, value) VALUES (?, ?)", k, v); err != nil {
	return fmt.Errorf("db: SetLocalMetadata %s: %w", k, err)
}
Defensive patterns

Strategy: try-catch

Validate before calling

if err := doctor.CheckSchema(ctx, repo); err != nil {
	return fmt.Errorf("schema not ready for local_metadata: %w", err)
}

Try / catch

if err := repo.SetLocalMetadata(ctx, key, value); err != nil {
	var drv *driver.Error
	if errors.As(err, &drv) {
		// inspect drv for lock/IO/table cause before retrying
	}
	return fmt.Errorf("persisting %q failed: %w", key, err)
}

Prevention

When it happens

Trigger: Calling SetLocalMetadata(ctx, key, value) when the REPLACE INTO local_metadata statement fails: database file corrupted or locked, local_metadata table missing (schema migration not run), connection dropped mid-write, or a driver-level constraint/IO error.

Common situations: Running bd commands against a .beads database whose schema predates the local_metadata table; disk full or database file corrupted after a crash; concurrent access locking the Dolt database; version mismatch where an older binary opens a newer schema.

Related errors


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

Appendix: source

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

	}
	return nil
}

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

func (r *configSQLRepositoryImpl) SetLocalMetadata(ctx context.Context, key, value string) error {
	if _, err := r.runner.ExecContext(ctx, "REPLACE INTO local_metadata (`key`, value) VALUES (?, ?)", key, value); err != nil {
		return fmt.Errorf("db: SetLocalMetadata %s: %w", key, err)
	}
	return nil
}

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" {

View on GitHub (pinned to 71377f2769)