gastownhall/beads · error

scan remote-tracking ref: %w

Error message

scan remote-tracking ref: %w

What it means

ListRemoteRefs iterates rows from dolt_remote_branches and scans each row's name column into a string. This error wraps a rows.Scan failure — the driver could not convert the row value into a *string, or the row is in an unexpected shape. It is thrown so the listing step reports per-row data problems distinctly from query errors.

Source

Thrown at internal/storage/versioncontrolops/remoterefs.go:21

import (
	"context"
	"fmt"
)

// ListRemoteRefs returns the names of all cached remote-tracking refs
// (e.g. "remotes/origin/main"), sorted by name.
func ListRemoteRefs(ctx context.Context, db DBConn) ([]string, error) {
	rows, err := db.QueryContext(ctx, "SELECT name FROM dolt_remote_branches ORDER BY name")
	if err != nil {
		return nil, fmt.Errorf("list remote-tracking refs: %w", err)
	}
	defer rows.Close()

	var refs []string
	for rows.Next() {
		var name string
		if err := rows.Scan(&name); err != nil {
			return nil, fmt.Errorf("scan remote-tracking ref: %w", err)
		}
		refs = append(refs, name)
	}
	return refs, rows.Err()
}

// PruneRemoteRefs deletes every cached remote-tracking ref and returns the
// names deleted. After a history squash (Flatten/Compact) these refs still
// anchor the pre-squash commit chain, so DOLT_GC treats the entire old history
// as reachable and reclaims nothing (bd-agctw). Pruning is safe on a squashed
// workspace: the refs are local caches only — nothing is deleted on the remote
// itself — and the next push or fetch re-creates them at the new tip.
//
// On error, the returned slice holds the refs deleted before the failure.
func PruneRemoteRefs(ctx context.Context, db DBConn) ([]string, error) {
	refs, err := ListRemoteRefs(ctx, db)
	if err != nil {
		return nil, err

View on GitHub (pinned to 71377f2769)

Solutions

  1. Inspect the wrapped error to identify the offending column/value type mismatch.
  2. Run SELECT name FROM dolt_remote_branches manually to find rows with NULL or malformed names and repair or remove them.
  3. Upgrade or re-initialize the Dolt database if metadata was corrupted by a version change (e.g. after a failed migration).
  4. Check rows.Err() after iteration to ensure the row set itself is intact.

Example fix

// before
if err := rows.Scan(&name); err != nil {
    return nil, fmt.Errorf("scan remote-tracking ref: %w", err)
}
// after: tolerate NULLs with sql.NullString
var name sql.NullString
if err := rows.Scan(&name); err != nil {
    return nil, fmt.Errorf("scan remote-tracking ref: %w", err)
}
if !name.Valid { continue }
Defensive patterns

Strategy: validation

Validate before calling

rows, err := db.QueryContext(ctx, "SELECT name FROM dolt_remote_branches WHERE name IS NOT NULL ORDER BY name")
if err != nil {
    return err
}

Try / catch

refs, err := versioncontrolops.ListRemoteRefs(ctx, db)
var scanErr *sql.ScanError
if err != nil && errors.As(err, &scanErr) {
    log.Printf("malformed ref row, falling back to raw query: %v", err)
    refs, err = queryRefsManually(ctx, db)
}

Prevention

When it happens

Trigger: A row in dolt_remote_branches has a NULL or non-string name value that cannot be scanned into a Go string; the driver returns rows in an unexpected column layout after a Dolt engine change.

Common situations: Corrupted or partially-migrated Dolt metadata after an upgrade/downgrade; a Dolt version where dolt_remote_branches exposes different column types; NULL ref names left by an interrupted operation.

Related errors


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