gastownhall/beads · error

db: GetLocalMetadata %s: %w

Error message

db: GetLocalMetadata %s: %w

What it means

GetLocalMetadata reads machine-local settings from the local_metadata table (values not synced across machines). As with GetMetadata, an absent key returns "", nil; this wrapper fires only when the SELECT fails outright: local_metadata table missing, database locked/corrupt, permission or connection problems, or a cancelled context.

Source

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

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

View on GitHub (pinned to 71377f2769)

Solutions

  1. Inspect the wrapped driver error to identify the concrete cause.
  2. Run bd doctor/migrations to create the local_metadata table if missing.
  3. Check file permissions, locks, and connectivity; close competing processes.
  4. Retry transient failures; remember a missing key returns empty string, not this error.

Example fix

// before
v, err := configRepo.GetLocalMetadata(ctx, "last_sync") // legacy DB lacks local_metadata
// after
if err := migrate.EnsureSchema(db); err != nil {
    return err
}
v, err := configRepo.GetLocalMetadata(ctx, "last_sync")
Defensive patterns

Strategy: fallback

Validate before calling

if err := runner.PingContext(ctx); err != nil {
    return fmt.Errorf("database unavailable: %w", err)
}

Type guard

null

Try / catch

v, err := configRepo.GetLocalMetadata(ctx, key)
if err != nil {
    if strings.Contains(err.Error(), "no such table") {
        // migrate: local_metadata table missing in this (legacy) database
    }
    return fmt.Errorf("local metadata read failed for %s: %w", key, err)
}
if v == "" {
    // key not set locally: fall back to default value
}

Prevention

When it happens

Trigger: QueryRowContext("SELECT value FROM local_metadata WHERE `key` = ?").Scan errors for any reason other than sql.ErrNoRows — typically a database created before local_metadata existed, a locked/corrupt file, or connectivity loss.

Common situations: Older databases not yet migrated to include local_metadata; concurrent access locking the SQLite/Dolt file; running bd where the DB file has wrong permissions.

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/baeb27681332251a. Report an issue: GitHub.