gastownhall/beads · error

legacy SQLite issue %s: %w

Error message

legacy SQLite issue %s: %w

What it means

During loadIssues, each scanned legacy issue is validated by issue.Validate(); any validation failure is wrapped as 'legacy SQLite issue <id>: <reason>'. This means the legacy row's data violates issue invariants (e.g. missing/invalid status, bad priority, empty ID, invalid field values), so the issue cannot be safely imported.

Source

Thrown at internal/migration/legacysqlite/reader.go:481

	}
	if err := checkCurrentInts(
		currentInt{"issue estimated_minutes", x.estimatedMinutes},
		currentInt{"issue compaction_level", x.compactionLevel},
		currentInt{"issue original_size", x.originalSize},
	); err != nil {
		return err
	}
	if err := x.applyCanonicalTimestamps(issue); err != nil {
		return err
	}
	if err := checkRequiredScalars(issue); err != nil {
		return err
	}
	if err := x.checkRemovedFields(issue); err != nil {
		return err
	}
	if err := issue.Validate(); err != nil {
		return fmt.Errorf("legacy SQLite issue %s: %w", issue.ID, err)
	}
	return nil
}

// applyOptionalTimestamps parses the legacy issue's optional timestamp columns
// (closed_at, compacted_at, due_at, defer_until) and assigns them to issue.
func (x legacyExtras) applyOptionalTimestamps(issue *types.Issue) error {
	var err error
	if issue.ClosedAt, err = parseOptionalTime("closed_at", x.closedAt); err != nil {
		return fmt.Errorf("legacy SQLite issue %s: %w", issue.ID, err)
	}
	if issue.CompactedAt, err = parseOptionalTime("compacted_at", x.compactedAt); err != nil {
		return fmt.Errorf("legacy SQLite issue %s: %w", issue.ID, err)
	}
	if issue.DueAt, err = parseOptionalTime("due_at", x.dueAt); err != nil {
		return fmt.Errorf("legacy SQLite issue %s: %w", issue.ID, err)
	}
	if issue.DeferUntil, err = parseOptionalTime("defer_until", x.deferUntil); err != nil {

View on GitHub (pinned to 71377f2769)

Solutions

  1. Read the wrapped inner message to see which field failed validation, then fix that row's column values in the legacy DB (e.g. normalize status/priority to current enum values).
  2. Identify the offending issue ID from the error and repair or delete the row, then re-run migration.
  3. Check whether the legacy DB predates a schema/vocabulary change and run any needed upgrade steps before migrating.
  4. Export the row, validate it offline, and re-import after correction.

Example fix

-- before (row fails validation)
UPDATE issues SET status='in_progress' WHERE id='bd-42';
-- after (canonical enum value)
UPDATE issues SET status='in_progress' WHERE id='bd-42' AND status IN ('open','in_progress','closed');
Defensive patterns

Strategy: validation

Validate before calling

// before migration, check enum values
rows, _ := db.Query(`SELECT id FROM issues WHERE status NOT IN ('open','in_progress','closed') OR priority NOT IN (0,1,2,3,4)`)
// fix reported rows first

Try / catch

if err := loadIssues(...); err != nil {
  var issueID string
  if _, scanErr := fmt.Sscanf(err.Error(), "legacy SQLite issue %s", &issueID); scanErr == nil {
    // inspect and repair that row in the legacy DB
  }
  return err
}

Prevention

When it happens

Trigger: Migrating a legacy SQLite DB where an issues row has field values that fail types.Issue.Validate — for example an unknown status enum value, invalid priority, out-of-range or malformed fields.

Common situations: Legacy DBs written by old beads versions whose status/priority vocabularies changed; rows corrupted by manual SQL edits; data imported from another tracker with non-canonical enum values; half-finished writes in the legacy DB.

Related errors


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