gastownhall/beads · error

scan dependent: %w

Error message

scan dependent: %w

What it means

While iterating rows returned by the external-dependents query, rows.Scan failed to decode the two expected string columns (depends_on_id, issue_id). This almost always means the row shape returned by the backend does not match the query — e.g. a NULL in a column scanned into a string, or a modified DepTargetExpr changing column type/arity. Rows are closed before returning.

Source

Thrown at internal/storage/issueops/delete_role.go:213

		}
		inClause, args := buildSQLInClause(ids[i:end])

		for _, depTable := range []string{"dependencies", "wisp_dependencies"} {
			rows, err := tx.QueryContext(ctx,
				fmt.Sprintf(`SELECT %s AS depends_on_id, issue_id FROM %s WHERE %s`,
					DepTargetExpr, depTable, depTargetIn("", inClause)),
				args...)
			if err != nil {
				if optionalBlockedTable(depTable) && isTableNotExistError(err) {
					continue
				}
				return nil, fmt.Errorf("query dependents from %s: %w", depTable, err)
			}
			for rows.Next() {
				var target, dependent string
				if err := rows.Scan(&target, &dependent); err != nil {
					_ = rows.Close()
					return nil, fmt.Errorf("scan dependent: %w", err)
				}
				if idSet[dependent] {
					continue
				}
				if bySource[target] == nil {
					bySource[target] = make(map[string]bool)
				}
				bySource[target][dependent] = true
			}
			_ = rows.Close()
			if err := rows.Err(); err != nil {
				return nil, fmt.Errorf("iterate dependents from %s: %w", depTable, err)
			}
		}
	}

	out := make(map[string][]string, len(bySource))
	for target, dependents := range bySource {

View on GitHub (pinned to 71377f2769)

Solutions

  1. Find the offending row: SELECT ... WHERE issue_id IS NULL OR target IS NULL on the named dependency table, and repair it
  2. Check whether DepTargetExpr was customized and still yields a plain string column
  3. Verify dependency rows were not hand-edited or written by an incompatible schema version
  4. Re-run the delete after repairing the row; the scan fails deterministically on the same row until fixed

Example fix

// before: NULL target breaks the string scan
rows, _ := tx.QueryContext(ctx, "SELECT dep_target, issue_id FROM dependencies WHERE ...")
// after (repair the data first):
UPDATE dependencies SET target_id = '<valid-id>' WHERE target_id IS NULL;
Defensive patterns

Strategy: validation

Validate before calling

// audit dependency rows for NULLs before deleting
rows, err := db.Query("SELECT issue_id, target_id FROM dependencies WHERE issue_id IS NULL OR target_id IS NULL")
// any returned row will crash the scan later; repair it first

Try / catch

if err != nil && strings.Contains(err.Error(), "scan dependent") {
    // find and fix the malformed dependency row, then re-run
}

Prevention

When it happens

Trigger: ExternalDependentsBySourceInTx loops rows.Next() on the dependencies/wisp_dependencies query and Scan(&target, &dependent) errors — NULL values in scanned columns, driver type mismatch, or altered SELECT shape.

Common situations: Dependency rows with NULL issue_id or NULL target expression written by an older version or external tool; a customized DepTargetExpr returning a non-string type; backend driver returning column types the scan cannot coerce.

Related errors


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