gastownhall/beads · error

get dependency counts: scan dependent: %w

Error message

get dependency counts: scan dependent: %w

What it means

Returned by GetDependencyCountsInTx when a row from the 'dependents counts' aggregate query (SELECT depends_on_id, COUNT(*) ... GROUP BY depends_on_id) cannot be scanned into (string, int). The wrapped error identifies the exact scan failure, typically a column type or nullability mismatch against the dependencies table.

Source

Thrown at internal/storage/issueops/dependency_queries.go:510

			//nolint:gosec // G201: depTable is hardcoded and inClause contains only ? placeholders.
			blockingRows, err := tx.QueryContext(ctx, fmt.Sprintf(`
				SELECT %s AS depends_on_id, COUNT(*) as cnt
				FROM %s
				WHERE %s AND type = 'blocks'
				GROUP BY %s
			`, DepTargetExpr, depTable, depTargetIn("", inClause), DepTargetExpr), args...)
			if err != nil {
				if optionalBlockedTable(depTable) && isTableNotExistError(err) {
					continue
				}
				return nil, fmt.Errorf("get dependency counts (dependents from %s): %w", depTable, err)
			}
			for blockingRows.Next() {
				var id string
				var cnt int
				if err := blockingRows.Scan(&id, &cnt); err != nil {
					_ = blockingRows.Close()
					return nil, fmt.Errorf("get dependency counts: scan dependent: %w", err)
				}
				if c, ok := result[id]; ok {
					c.DependentCount += cnt
				}
			}
			_ = blockingRows.Close()
			if err := blockingRows.Err(); err != nil {
				return nil, fmt.Errorf("get dependency counts: dependent rows: %w", err)
			}
		}
	}

	return result, nil
}

// GetBlockingInfoForIssuesInTx returns blocking dependency records for a set of issue IDs.
// Returns three maps:
//   - blockedByMap: issueID -> list of IDs blocking it

View on GitHub (pinned to 71377f2769)

Solutions

  1. Read the wrapped scan error to find which column/value failed and locate the offending row.
  2. Delete or repair rows with NULL depends_on_id in dependencies/wisp_dependencies (SELECT ... WHERE depends_on_id IS NULL).
  3. Re-run schema migration (bd doctor / migrations) to restore expected column types and nullability.
  4. Restore from a known-good backup if rows are corrupted.
  5. Ensure driver and server versions match to avoid type-conversion differences.

Example fix

// before: NULL depends_on_id rows break the scan
SELECT * FROM dependencies WHERE depends_on_id IS NULL;

// after: remove or repair offending rows
DELETE FROM dependencies WHERE depends_on_id IS NULL;
Defensive patterns

Strategy: validation

Validate before calling

// Go: detect rows that will fail the scan beforehand
rows, err := tx.QueryContext(ctx,
    "SELECT depends_on_id FROM dependencies WHERE depends_on_id IS NULL")
if err != nil { return err }
defer rows.Close()
if rows.Next() {
    return fmt.Errorf("dependencies has NULL depends_on_id rows; repair before querying")
}

Type guard

func isScanError(err error) bool {
    return err != nil && strings.Contains(err.Error(), "scan dependent")
}

Try / catch

counts, err := GetDependencyCountsInTx(ctx, tx, ids)
if err != nil && strings.Contains(err.Error(), "scan dependent") {
    // repair NULL rows, then retry once
    if _, fixErr := tx.ExecContext(ctx,
        "DELETE FROM dependencies WHERE depends_on_id IS NULL"); fixErr != nil {
        return fixErr
    }
    counts, err = GetDependencyCountsInTx(ctx, tx, ids)
}

Prevention

When it happens

Trigger: Calling GetDependencyCountsInTx against a schema where depends_on_id (via DepTargetExpr) or the COUNT column returns NULL or an unexpected type — e.g. rows hand-inserted with NULL depends_on_id, or a table altered to a type the driver cannot convert to string.

Common situations: Manual SQL edits inserting NULL depends_on_id rows; schema drift after partial migration; driver version incompatibilities changing type mapping; corrupted rows in a Dolt database restored from a bad backup.

Related errors


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