gastownhall/beads · error

scan dependent: %w

Error message

scan dependent: %w

What it means

While iterating dependents rows in FindAllDependentsInTx, rows.Scan into a single string column (issue_id) failed. This means the result row could not be decoded into a string — usually a driver-level decode error or an unexpected column shape, not a data problem.

Source

Thrown at internal/storage/issueops/delete.go:442

		toProcess = toProcess[batchEnd:]

		inClause, args := buildSQLInClause(batch)
		for _, depTable := range []string{"dependencies", "wisp_dependencies"} {
			rows, err := tx.QueryContext(ctx,
				fmt.Sprintf(`SELECT issue_id FROM %s WHERE %s`, depTable, depTargetIn("", inClause)),
				args...)
			if err != nil {
				if optionalBlockedTable(depTable) && isTableNotExistError(err) {
					continue
				}
				return nil, fmt.Errorf("query dependents for batch from %s: %w", depTable, err)
			}

			for rows.Next() {
				var depID string
				if err := rows.Scan(&depID); err != nil {
					_ = rows.Close()
					return nil, fmt.Errorf("scan dependent: %w", err)
				}
				if !result[depID] {
					result[depID] = true
					toProcess = append(toProcess, depID)
				}
			}
			_ = rows.Close()
			if err := rows.Err(); err != nil {
				return nil, fmt.Errorf("iterate dependents for batch from %s: %w", depTable, err)
			}
		}
	}

	return result, nil
}

//nolint:gosec // G201: table is selected by callers from fixed issue/wisp auxiliary tables.
func CountRowsForIssueIDsInTx(ctx context.Context, tx DBTX, table string, ids []string) (int, error) {

View on GitHub (pinned to 71377f2769)

Solutions

  1. Find and repair the offending row: SELECT * FROM dependencies WHERE issue_id IS NULL and fix or delete it
  2. Re-run bd doctor / integrity checks to detect corrupt dependency rows
  3. Re-import or re-sync the dependency data if corruption came from a bad sync
  4. Update the storage driver if a type-mismatch bug is implicated

Example fix

-- before (corrupt row)
INSERT INTO dependencies (issue_id, depends_on_id) VALUES (NULL, 'bd-1');
-- after
INSERT INTO dependencies (issue_id, depends_on_id) VALUES ('bd-2', 'bd-1');
Defensive patterns

Strategy: validation

Validate before calling

// detect corrupt dependency rows before deleting
rows, _ := db.Query("SELECT issue_id FROM dependencies WHERE issue_id IS NULL")
// any result => repair rows before cascade delete

Try / catch

_, err := DeleteIssuesInTx(ctx, tx, ids, WithCascade())
if err != nil && strings.Contains(err.Error(), "scan dependent:") {
	repairNullDependencyRows(db) // fix or remove bad rows, then retry
	return retry(ctx, ids)
}
return err

Prevention

When it happens

Trigger: Cascade traversal (ResolveDeletionSetInTx → FindAllDependentsInTx) where the SELECT returns a row whose value cannot scan into a string — e.g. NULL issue_id in a corrupted dependency row, or a driver type mismatch.

Common situations: Corrupted dependency rows after a failed import/sync inserting NULL or non-string issue_id values; driver version mismatch producing unexpected column types.

Related errors


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