gastownhall/beads · error

scan issue ID: %w

Error message

scan issue ID: %w

What it means

While iterating the source_repo id results, rows.Scan(&id) failing produces "scan issue ID: %w". Since only a single text id column is selected, this almost always means the id column is NULL or returned in a type the driver cannot convert to string.

Source

Thrown at internal/storage/issueops/bulk_ops.go:185

		_ = rows.Close()
	}
	return nil
}

// DeleteIssuesBySourceRepoInTx removes all issues from a source repo and their related data.
//
//nolint:gosec // G201: table is validated by hardcoded list
func DeleteIssuesBySourceRepoInTx(ctx context.Context, tx *sql.Tx, sourceRepo string) (int, error) {
	rows, err := tx.QueryContext(ctx, `SELECT id FROM issues WHERE source_repo = ?`, sourceRepo)
	if err != nil {
		return 0, fmt.Errorf("query issues: %w", err)
	}
	var issueIDs []string
	for rows.Next() {
		var id string
		if err := rows.Scan(&id); err != nil {
			_ = rows.Close()
			return 0, fmt.Errorf("scan issue ID: %w", err)
		}
		issueIDs = append(issueIDs, id)
	}
	_ = rows.Close()

	if len(issueIDs) == 0 {
		return 0, nil
	}

	affectedIssues, affectedWisps, aerr := AffectedByDeletionInTx(ctx, tx, issueIDs, nil)
	if aerr != nil {
		return 0, fmt.Errorf("affected by source-repo delete: %w", aerr)
	}

	// Deleted issues hold no leases: clear them while the id set is still
	// joinable (before the issues rows go away).
	if _, err := tx.ExecContext(ctx,
		`DELETE FROM leases WHERE issue_id IN (SELECT id FROM issues WHERE source_repo = ?)`, sourceRepo); err != nil {

View on GitHub (pinned to 71377f2769)

Solutions

  1. Inspect the wrapped error — likely "converting NULL to string is unsupported" — and find/fix the NULL id row
  2. Restore from backup or re-run integrity checks (e.g. PRAGMA integrity_check) if the DB is corrupted
  3. Run pending migrations to rebuild any malformed rows
  4. If a custom driver is in use, ensure it returns ids in a string-convertible type

Example fix

null
Defensive patterns

Strategy: type-guard

Validate before calling

// detect corrupt rows before the bulk delete
rows, _ := db.Query("SELECT COUNT(*) FROM issues WHERE id IS NULL OR id = ''")

Type guard

var convErr *sql.ConvertError
if errors.As(err, &convErr) { /* convErr identifies the bad value/column */ }

Try / catch

if err != nil && strings.Contains(err.Error(), "converting NULL") {
    // stop and repair the database; do not proceed with a partial delete
    return fmt.Errorf("corrupt issue id row: %w", err)
}

Prevention

When it happens

Trigger: rows.Scan errors on the single id column: NULL id in the issues table (corrupted/manually modified DB), or driver type conversion failure for the id value.

Common situations: Corrupted database from a crashed legacy migration; manually edited rows with NULL primary keys; a non-SQLite driver returning ids as non-string types.

Related errors


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