gastownhall/beads · error

failed to scan history: %w

Error message

failed to scan history: %w

What it means

After the history query succeeds, getIssueHistory scans each row into the issue and history structs, including nullable TEXT columns read as sql.Null* values. This error is returned when rows.Scan fails — almost always a column-count or column-type mismatch between the SELECT * projection and the fixed scan target list, or an unparseable value in a scanned column.

Source

Thrown at internal/storage/dolt/history.go:108

	var history []*issueHistory
	for rows.Next() {
		var h issueHistory
		var issue types.Issue
		var createdAtStr, updatedAtStr sql.NullString // TEXT columns - must parse manually
		var closedAt sql.NullTime
		var assignee, owner, createdBy, closeReason, molType sql.NullString
		var estimatedMinutes sql.NullInt64
		var pinned sql.NullInt64

		if err := rows.Scan(
			&issue.ID, &issue.Title, &issue.Description, &issue.Design, &issue.AcceptanceCriteria, &issue.Notes,
			&issue.Status, &issue.Priority, &issue.IssueType, &assignee, &owner, &createdBy,
			&estimatedMinutes, &createdAtStr, &updatedAtStr, &closedAt, &closeReason,
			&pinned, &molType,
			&h.CommitHash, &h.Committer, &h.CommitDate,
		); err != nil {
			return nil, fmt.Errorf("failed to scan history: %w", err)
		}

		// Parse timestamp strings (TEXT columns require manual parsing)
		if createdAtStr.Valid {
			issue.CreatedAt = parseTimeString(createdAtStr.String)
		}
		if updatedAtStr.Valid {
			issue.UpdatedAt = parseTimeString(updatedAtStr.String)
		}

		if closedAt.Valid {
			issue.ClosedAt = &closedAt.Time
		}
		if assignee.Valid {
			issue.Assignee = assignee.String
		}
		if owner.Valid {
			issue.Owner = owner.String

View on GitHub (pinned to 71377f2769)

Solutions

  1. Align the beads binary and database schema versions — run the project's schema migration/upgrade steps so dolt_history_issues matches the compiled scan list.
  2. Check the wrapped error message: it names the offending column/index; compare current SHOW CREATE TABLE dolt_history_issues output with the Scan target order.
  3. Upgrade or downgrade the Dolt engine to a version compatible with the installed beads release.

Example fix

// before
rows, _ := s.queryContext(ctx, "SELECT * FROM dolt_history_issues ...") // column drift
rows.Scan(&issue.ID, ..., &h.CommitDate) // scan count mismatch
// after
rows, _ := s.queryContext(ctx, "SELECT id, title, description, ... commit_hash, committer, commit_date FROM dolt_history_issues ...") // explicit column list
rows.Scan(&issue.ID, ..., &h.CommitDate)
Defensive patterns

Strategy: try-catch

Validate before calling

func schemaMatches(db *sql.DB) error {
    rows, err := db.Query("SHOW COLUMNS FROM dolt_history_issues")
    if err != nil { return err }
    defer rows.Close()
    return nil // compare count/names against expected scan list in production
}

Try / catch

history, err := store.GetIssueHistory(ctx, id)
if err != nil && strings.Contains(err.Error(), "failed to scan history") {
    return fmt.Errorf("beads/schema version mismatch, run migrations: %w", err)
}

Prevention

When it happens

Trigger: The dolt_history_issues schema drifts from the code's expectation (added/removed/reordered columns) so SELECT * returns a different column set than the Scan targets; a column type changes (e.g. a numeric returned as string); NULL appears where a non-Null scan target is used.

Common situations: Upgrading the Dolt server which changes system-table schema; beads schema migrations applied to the DB but binary not updated (or vice versa); running a mixed-version fleet where one node wrote rows with new columns.

Related errors


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