gastownhall/beads · error

count %s orphans: %w

Error message

count %s orphans: %w

What it means

For each spec'd FK whose constraint is missing, scanSeveredCloneLocalFKs counts orphaned rows via a generated NOT EXISTS query (identifiers come from the fixed CloneLocalFKs spec, not user input). If that count query fails, the scan returns this wrapped error instead of a SeveredCloneLocalFK entry.

Source

Thrown at cmd/bd/doctor/fix/clone_local_fks.go:102

		if err := db.QueryRow(
			`SELECT COUNT(*) FROM information_schema.TABLE_CONSTRAINTS
			 WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = ? AND CONSTRAINT_NAME = ? AND CONSTRAINT_TYPE = 'FOREIGN KEY'`,
			fk.Table, fk.Constraint,
		).Scan(&constraints); err != nil {
			return nil, fmt.Errorf("check %s.%s: %w", fk.Table, fk.Constraint, err)
		}
		if constraints > 0 {
			continue
		}

		var orphans int
		//nolint:gosec // G201: identifiers come from the fixed CloneLocalFKs spec above, not user input.
		orphanCount := fmt.Sprintf(
			`SELECT COUNT(*) FROM %s t WHERE t.%s IS NOT NULL AND NOT EXISTS (SELECT 1 FROM %s r WHERE r.%s = t.%s)`,
			fk.Table, fk.Column, fk.RefTable, fk.RefColumn, fk.Column,
		)
		if err := db.QueryRow(orphanCount).Scan(&orphans); err != nil {
			return nil, fmt.Errorf("count %s orphans: %w", fk.Table, err)
		}

		severed = append(severed, SeveredCloneLocalFK{CloneLocalFK: fk, Orphans: orphans})
	}
	return severed, nil
}

// CloneLocalFKEnforcement re-links severed clone-local FKs: for each missing
// constraint it deletes the orphaned rows that accumulated while enforcement
// was off (ADD CONSTRAINT validates existing rows, so they must go first),
// then re-adds the constraint in place. Verified on dolt 2.2.2: the re-added
// FK resolves against the current tracked root and enforces again.
func CloneLocalFKEnforcement(path string, verbose bool) error {
	beadsDir, err := resolvedWorkspaceBeadsDir(path)
	if err != nil {
		return err
	}

View on GitHub (pinned to 71377f2769)

Solutions

  1. Inspect the wrapped driver error and re-run `bd doctor` after the storage issue is resolved.
  2. Verify the actual schema matches the CloneLocalFKs spec (table/column names) after any manual migration.
  3. Check for disk/lock issues in the .beads Dolt store and free space or clear locks.
  4. Reduce scan size by repairing one workspace at a time if timeouts are the cause.
Defensive patterns

Strategy: validation

Validate before calling

// Confirm spec'd tables/columns still exist before the orphan count runs:
rows, _ := db.Query(`SELECT TABLE_NAME, COLUMN_NAME FROM information_schema.COLUMNS`)
// compare against CloneLocalFKs entries; mismatch = schema drift to fix first

Try / catch

severed, err := ScanSeveredCloneLocalFKs(ctx, db)
if err != nil && strings.Contains(err.Error(), "count") {
	// orphan-count failure: fix schema drift or storage, then rescan
	return err
}

Prevention

When it happens

Trigger: The dynamically built orphan-count SELECT fails in scanSeveredCloneLocalFKs (cmd/bd/doctor/fix/clone_local_fks.go:102) — typically a storage/driver failure mid-scan or a table that exists but cannot be read.

Common situations: Large orphan scans hitting storage timeouts, a partially migrated schema where fk.Column/fk.RefTable no longer match the actual tables, or Dolt read errors on a cloned repo.

Understand the failure class

Background: Database query failed: Internal Server Error 500s wrapping SQL, Prisma, and connection failures — what to check first — this error's family across 16 libraries.

Related errors


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