gastownhall/beads · error

db: Get %s: %w

Error message

db: Get %s: %w

What it means

This error wraps any unexpected failure from the repository's Get path, adding the issue ID that was being fetched. It is thrown after scanIssue fails with an error other than sql.ErrNoRows (not-found is returned verbatim as sql.ErrNoRows, not wrapped). Callers like Update and Claim rely on Get to load the row before mutating it, so a wrapped driver/scan failure aborts the whole operation.

Source

Thrown at internal/storage/domain/db/issue.go:564

		StartedAtWasZero: startedWasZero,
		OldIssue:         oldIssue,
	}, nil
}

func (r *issueSQLRepositoryImpl) Get(ctx context.Context, id string, opts domain.IssueTableOpts) (*types.Issue, error) {
	if id == "" {
		return nil, errors.New("db: Get: id must not be empty")
	}
	table := pickIssueTable(opts.UseWispsTable)
	//nolint:gosec // G201: table is one of two hardcoded constants
	row := r.runner.QueryRowContext(ctx, fmt.Sprintf("SELECT %s FROM %s %s WHERE id = ?",
		issueSelectColumns, table, sqlbuild.LeaseJoin(table)), id)
	issue, err := scanIssue(row)
	if errors.Is(err, sql.ErrNoRows) {
		return nil, sql.ErrNoRows
	}
	if err != nil {
		return nil, fmt.Errorf("db: Get %s: %w", id, err)
	}
	return issue, nil
}

func (r *issueSQLRepositoryImpl) GetByIDs(ctx context.Context, ids []string, opts domain.IssueTableOpts) ([]*types.Issue, error) {
	if len(ids) == 0 {
		return nil, nil
	}
	placeholders := make([]string, len(ids))
	args := make([]any, len(ids))
	for i, id := range ids {
		placeholders[i] = "?"
		args[i] = id
	}
	table := pickIssueTable(opts.UseWispsTable)
	//nolint:gosec // G201: table is one of two hardcoded constants
	q := fmt.Sprintf("SELECT %s FROM %s %s WHERE id IN (%s)",
		issueSelectColumns, table, sqlbuild.LeaseJoin(table), strings.Join(placeholders, ","))

View on GitHub (pinned to 71377f2769)

Solutions

  1. Read the wrapped cause (%w chain) to identify the driver error; fix the underlying DB problem (connection, schema, permissions).
  2. Verify the DB schema matches the issueSelectColumns the code expects; run any provided migration/doctor command.
  3. Check connectivity to the Dolt server and retry transient failures (network blips, context timeouts).
  4. If scanning fails consistently for one issue, inspect that row for data that violates expected column types.

Example fix

// before
issue, err := repo.Get(ctx, id)
if err != nil { return err } // loses ErrNoRows distinction
// after
issue, err := repo.Get(ctx, id)
if err != nil {
    if errors.Is(err, sql.ErrNoRows) { return storage.ErrNotFound }
    return fmt.Errorf("get issue %s: %w", id, err)
}
Defensive patterns

Strategy: try-catch

Validate before calling

// verify connectivity before calling
if err := sqlDB.PingContext(ctx); err != nil { return fmt.Errorf("db unavailable: %w", err) }

Try / catch

issue, err := repo.Get(ctx, id)
if err != nil {
    if errors.Is(err, sql.ErrNoRows) { return storage.ErrNotFound }
    var transient = isTransientDBErr(err) // inspect wrapped cause
    if transient { return retry(...) }
    return fmt.Errorf("get issue %s: %w", id, err)
}

Prevention

When it happens

Trigger: Calling Get, Update, or Claim on issues via IssueSQLRepository when the underlying SELECT (with lease join) fails: driver-level query errors, connection drops, scan/type-mismatch errors, or corrupted rows. Not raised for missing IDs — those surface as sql.ErrNoRows.

Common situations: Database connection dropped mid-operation, schema drift after an upgrade (columns renamed/moved so scanIssue fails), permission changes revoking SELECT on issues, or context cancellation surfacing as a driver error.

Related errors


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