gastownhall/beads · error
legacy SQLite issue %s uses unsupported removed fields
Error message
legacy SQLite issue %s uses unsupported removed fields
What it means
checkRemovedFields rejects legacy rows that populate columns the current schema no longer supports (closedBy, deletedBy, deleteReason, originalType, hookBead, roleBead, agentState, lastActivity, roleType, rig, deletedAt, crystallizes, quality), or whose SourceRepo is set to something other than empty/'.'. The migration cannot carry these fields forward, so the row fails validation.
Source
Thrown at internal/migration/legacysqlite/reader.go:568
// present created_at/updated_at invariants every legacy issue must satisfy.
func checkRequiredScalars(issue *types.Issue) error {
if issue.ID == "" {
return fmt.Errorf("legacy SQLite issue has empty ID")
}
if issue.Status == "tombstone" {
return fmt.Errorf("legacy SQLite issue %s is a tombstone", issue.ID)
}
if issue.CreatedAt.IsZero() || issue.UpdatedAt.IsZero() {
return fmt.Errorf("legacy SQLite issue %s has invalid created_at or updated_at", issue.ID)
}
return nil
}
// checkRemovedFields rejects legacy rows that populate columns the current
// schema no longer supports, then validates the tri-state boolean columns.
func (x legacyExtras) checkRemovedFields(issue *types.Issue) error {
if nonempty(x.closedBy, x.deletedBy, x.deleteReason, x.originalType, x.hookBead, x.roleBead, x.agentState, x.lastActivity, x.roleType, x.rig) || x.deletedAt.Valid || x.crystallizes.Int64 != 0 || x.quality.Valid || (issue.SourceRepo != "" && issue.SourceRepo != ".") {
return fmt.Errorf("legacy SQLite issue %s uses unsupported removed fields", issue.ID)
}
for _, b := range []struct {
name string
v sql.NullInt64
}{{"ephemeral", x.ephemeral}, {"pinned", x.pinned}, {"is_template", x.template}} {
if b.v.Valid && b.v.Int64 != 0 && b.v.Int64 != 1 {
return fmt.Errorf("issue %s has invalid %s boolean", issue.ID, b.name)
}
}
return nil
}
type currentVarchar struct {
name, value string
maxRunes int
}
type currentString struct {View on GitHub (pinned to 71377f2769)
Solutions
- Identify which field is populated: sqlite3 legacy.db "SELECT * FROM issues WHERE id='<id>'" and compare against the removed-column list
- Clear/NULL the unsupported columns in the legacy DB before migrating
- Move needed information into supported fields (e.g. append deleteReason/agentState content into the issue description or notes) or a sidecar file, since the current schema stores such data as metadata, not columns
- Re-run the migration
Example fix
// before UPDATE issues SET agent_state='running', source_repo='/path/to/repo' WHERE id='<id>'; // after UPDATE issues SET agent_state=NULL, source_repo=NULL WHERE id='<id>'; -- preserve info in description instead: UPDATE issues SET description = description || '\n(agent_state was: running)' WHERE id='<id>';
Defensive patterns
Strategy: validation
Validate before calling
-- run before migration against the removed-column list SELECT id FROM issues WHERE closed_by IS NOT NULL OR deleted_by IS NOT NULL OR delete_reason IS NOT NULL OR original_type IS NOT NULL OR hook_bead IS NOT NULL OR role_bead IS NOT NULL OR agent_state IS NOT NULL OR last_activity IS NOT NULL OR role_type IS NOT NULL OR rig IS NOT NULL OR deleted_at IS NOT NULL OR crystallizes != 0 OR (source_repo IS NOT NULL AND source_repo != '' AND source_repo != '.');
Type guard
REMOVED_COLUMNS = ['closed_by','deleted_by','delete_reason','original_type','hook_bead','role_bead','agent_state','last_activity','role_type','rig']
def uses_removed_fields(row) -> bool:
if any(row.get(c) for c in REMOVED_COLUMNS):
return True
sr = row.get('source_repo')
return bool(sr) and sr != '.' Prevention
- Inventory legacy columns vs. the current schema before each migration
- Relocate needed info into supported fields (description/metadata) ahead of time
- Avoid experimental builds for production data
- Keep source_repo empty/'.' unless the current schema supports it
When it happens
Trigger: Migration reads a legacy issue row where any removed column is non-empty/non-default — e.g. agentState set by an orchestration layer, quality populated by a review tool, or source_repo pointing at a real repo path.
Common situations: Databases written by newer/experimental beads builds with features later removed; rows touched by external agents (GT/rig tooling) that populated role/rig columns; source_repo set by multi-repo setups.
Related errors
- legacy SQLite release marker: %w
- legacy SQLite schema drift in %s hidden column
- legacy SQLite schema drift in %s
- clone from %s succeeded, but the database needs %d schema %s
- sealed legacy SQLite database does not match source fingerpr
AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30).
Data as JSON: /api/errors/8e25cb324ed66fda.
Report an issue: GitHub.