gastownhall/beads · error

db: GetMetadata %s: %w

Error message

db: GetMetadata %s: %w

What it means

GetMetadata reads a single row from the shared metadata table by key. A missing key is not an error (it returns "", nil), so this wrapper only fires when the SELECT itself fails: database unavailable, metadata table missing (uninitialized/pre-migration schema), permission denied, or context cancellation. The failing key is embedded in the message for diagnosis.

Source

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

func NewConfigSQLRepository(runner Runner) domain.ConfigSQLRepository {
	return &configSQLRepositoryImpl{runner: runner}
}

type configSQLRepositoryImpl struct {
	runner Runner
}

var _ domain.ConfigSQLRepository = (*configSQLRepositoryImpl)(nil)

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

func (r *configSQLRepositoryImpl) SetMetadata(ctx context.Context, key, value string) error {
	if _, err := r.runner.ExecContext(ctx, "REPLACE INTO metadata (`key`, value) VALUES (?, ?)", key, value); err != nil {
		return fmt.Errorf("db: SetMetadata %s: %w", key, err)
	}
	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 {

View on GitHub (pinned to 71377f2769)

Solutions

  1. Inspect the wrapped cause (%w) for the concrete driver error.
  2. Run bd doctor / migrations to ensure the metadata table exists in the target database.
  3. Check database connectivity, file permissions, and that the DB isn't locked by another process.
  4. Retry transient failures; treat empty-string (not error) as the signal for a missing key.

Example fix

// before
v, err := configRepo.GetMetadata(ctx, "issue_prefix") // fails on legacy DB
// after
if err := migrate.EnsureSchema(db); err != nil {
    return err
}
v, err := configRepo.GetMetadata(ctx, "issue_prefix")
Defensive patterns

Strategy: fallback

Validate before calling

// verify DB reachability and schema before depending on metadata
if err := runner.PingContext(ctx); err != nil {
    return fmt.Errorf("database unavailable: %w", err)
}

Type guard

null

Try / catch

v, err := configRepo.GetMetadata(ctx, key)
if err != nil {
    if strings.Contains(err.Error(), "no such table") {
        // run bd doctor / migrations to create the metadata table
    }
    return fmt.Errorf("metadata read failed for %s: %w", key, err)
}
if v == "" {
    // key absent: use default — this is NOT an error
}

Prevention

When it happens

Trigger: QueryRowContext("SELECT value FROM metadata WHERE `key` = ?").Scan returns an error other than sql.ErrNoRows — e.g. corrupt/locked Dolt or SQLite file, metadata table absent in a fresh or legacy database, or the connection dropped mid-query.

Common situations: Running bd against a database created by an older version without the metadata table; read-only filesystem/permissions; network blip to a remote Dolt server; database locked by another writer.

Understand the failure class

Background: "query failed", "%w: SQL error" — wrapped database query errors in Go libraries explained — this error's family across 3 libraries.

Related errors


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