gastownhall/beads · error

checking %s.content_hash: %w

Error message

checking %s.content_hash: %w

What it means

hasContentHashColumn probes whether the migration cursor table has a content_hash column via `SHOW COLUMNS ... LIKE 'content_hash'`. When that SHOW COLUMNS query itself fails with an error that is NOT a table-does-not-exist error (table-not-exist is deliberately treated as 'false, nil'), the failure is wrapped as `checking <cursor>.content_hash: <cause>`.

Source

Thrown at internal/storage/schema/schema.go:1104

// hasContentHashColumn reports whether the cursor table already carries the
// content_hash column. A not-yet-created table simply reports false.
//
// It probes a single table with SHOW COLUMNS rather than INFORMATION_SCHEMA.COLUMNS,
// whose predicate Dolt does not push down. The LIKE narrows the result set, but
// we still compare the Field name exactly because '_' is a LIKE single-character
// wildcard.
func (m migrationSource) hasContentHashColumn(ctx context.Context, db DBConn) (bool, error) {
	//nolint:gosec // G201: m.cursorTable is a hardcoded constant; the LIKE literal is fixed.
	rows, err := db.QueryContext(ctx, "SHOW COLUMNS FROM "+m.cursorTable+" LIKE 'content_hash'")
	if err != nil {
		// SHOW COLUMNS errors on a missing table; the old INFORMATION_SCHEMA
		// probe returned count 0 instead. Preserve that: an absent cursor table
		// has no content_hash column.
		if dberrors.IsTableNotExist(err) {
			return false, nil
		}
		return false, fmt.Errorf("checking %s.content_hash: %w", m.cursorTable, err)
	}
	defer func() { _ = rows.Close() }()

	cols, err := rows.Columns()
	if err != nil {
		return false, fmt.Errorf("checking %s.content_hash: %w", m.cursorTable, err)
	}
	// SHOW COLUMNS returns Field, Type, Null, Key, Default, Extra (and possibly
	// more on some servers); scan every column into RawBytes and read the first
	// ("Field"), which is the column name.
	cells := make([]sql.RawBytes, len(cols))
	dest := make([]any, len(cols))
	for i := range cells {
		dest[i] = &cells[i]
	}
	for rows.Next() {
		if err := rows.Scan(dest...); err != nil {
			return false, fmt.Errorf("checking %s.content_hash: %w", m.cursorTable, err)

View on GitHub (pinned to 71377f2769)

Solutions

  1. Check the wrapped cause for connection/permission errors and fix connectivity or GRANTs (the DB user needs SHOW/SELECT on the schema).
  2. If the cause indicates a missing table unexpectedly, verify the cursor table name and that bootstrapSQL ran (CREATE TABLE IF NOT EXISTS).
  3. Retry the migration on a fresh connection — pooled Dolt sessions can hold stale catalog snapshots after a failed statement.
  4. Confirm you are on a supported Dolt server version; SHOW COLUMNS behavior differences can cause parse errors.

Example fix

// before
rows, err := db.QueryContext(ctx, "SHOW COLUMNS FROM "+m.cursorTable+" LIKE 'content_hash'")
// after
if err := m.ensureBootstrapTable(ctx, db); err != nil { // CREATE TABLE IF NOT EXISTS first
    return false, err
}
rows, err := db.QueryContext(ctx, "SHOW COLUMNS FROM "+m.cursorTable+" LIKE 'content_hash'")
Defensive patterns

Strategy: try-catch

Validate before calling

// verify access before bootstrap
rows, err := db.QueryContext(ctx, "SELECT 1 FROM information_schema.tables WHERE table_schema = DATABASE() LIMIT 1")
if err != nil {
    return fmt.Errorf("cannot read schema metadata: %w", err)
}
rows.Close()

Type guard

func isTableNotExist(err error) bool { return dberrors.IsTableNotExist(err) }
// treat isTableNotExist(err)==true as benign (column absent), everything else needs handling

Try / catch

_, err := MigrateUp(ctx, db)
if err != nil && strings.Contains(err.Error(), ".content_hash") {
    if !dberrors.IsTableNotExist(errors.Unwrap(err)) {
        log.Printf("content_hash probe failed (conn/perm issue): %v — retrying on fresh connection", err)
        _, err = MigrateUp(ctx, freshConnDB())
    }
}

Prevention

When it happens

Trigger: The `SHOW COLUMNS FROM <cursorTable> LIKE 'content_hash'` query fails for reasons other than a missing table: connection loss, permission denial, a corrupted catalog, or the session being poisoned after a prior failed statement on a pooled Dolt connection.

Common situations: Database user lacks privileges to run SHOW on the schema; transient network drop during bootstrap; Dolt server restarted mid-operation; the query runs against a backend where SHOW COLUMNS syntax/behavior differs.

Related errors


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