gastownhall/beads · error

journal: scan derived is_blocked from %s: %w

Error message

journal: scan derived is_blocked from %s: %w

What it means

While iterating the is_blocked snapshot rows, rows.Scan(&id, &blocked) failed; the rowset is closed and the transaction fails. This means a returned row's values could not be scanned into (string, int).

Source

Thrown at internal/storage/issueops/journal.go:240

				end = len(target.ids)
			}
			inClause, args := buildSQLInClause(target.ids[start:end])
			//nolint:gosec // table is one of the two hardcoded values above.
			rows, err := tx.QueryContext(ctx,
				fmt.Sprintf("SELECT id, is_blocked FROM %s WHERE id IN (%s)", target.table, inClause),
				args...)
			if err != nil {
				if optionalBlockedTable(target.table) && isTableNotExistError(err) {
					break
				}
				return nil, fmt.Errorf("journal: snapshot derived is_blocked from %s: %w", target.table, err)
			}
			for rows.Next() {
				var id string
				var blocked int
				if err := rows.Scan(&id, &blocked); err != nil {
					_ = rows.Close()
					return nil, fmt.Errorf("journal: scan derived is_blocked from %s: %w", target.table, err)
				}
				snapshot[blockedJournalKey{table: target.table, id: id}] = blocked != 0
			}
			if err := rows.Err(); err != nil {
				_ = rows.Close()
				return nil, fmt.Errorf("journal: iterate derived is_blocked from %s: %w", target.table, err)
			}
			if err := rows.Close(); err != nil {
				return nil, fmt.Errorf("journal: close derived is_blocked from %s: %w", target.table, err)
			}
		}
	}
	return snapshot, nil
}

// recordBlockedJournalChanges compares the stable post-maintenance state with
// the captured state and journals only beads whose derived is_blocked value
// actually changed. The emitted update carries the complete post-mutation

View on GitHub (pinned to 71377f2769)

Solutions

  1. Inspect the wrapped error to identify the column
  2. Fix the schema: is_blocked must be NOT NULL integer and id NOT NULL string
  3. Backfill NULL is_blocked values to 0/1
  4. Add a migration if upgrading across versions

Example fix

// before
var blocked int
if err := rows.Scan(&id, &blocked); err != nil { ... }
// after
var blocked sql.NullInt64
if err := rows.Scan(&id, &blocked); err != nil { ... }
blockedVal := blocked.Int != 0
Defensive patterns

Strategy: try-catch

Validate before calling

// verify column types before journal operations
rows, _ := db.Query("SELECT id, is_blocked FROM deps LIMIT 1")
var id string; var blocked sql.NullInt64
if rows.Next() && rows.Scan(&id, &blocked) != nil { /* schema drift: is_blocked not int */ }

Type guard

null

Try / catch

snap, err := captureBlockedJournalSnapshot(ctx, tx)
if err != nil {
  log.Printf("blocked snapshot scan failed: %v", err)
  return err // abort transaction; fix schema before retry
}

Prevention

When it happens

Trigger: is_blocked column is NULL (Scan into int fails) or has a non-integer type (e.g. TEXT) after schema drift; id column NULL or type mismatch.

Common situations: Schema changed is_blocked from NOT NULL to nullable or from INT to another type; manual schema edits; data imported from another tool.

Related errors


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