gastownhall/beads · error
issue %s has invalid %s boolean
Error message
issue %s has invalid %s boolean
What it means
checkRemovedFields validates the tri-state boolean columns ephemeral, pinned, and is_template. Each may be NULL (unset), 0, or 1; any other non-zero integer (e.g. 2, -1) is invalid. This error names the offending issue and the specific boolean column with an out-of-range value.
Source
Thrown at internal/migration/legacysqlite/reader.go:575
}
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 {
name, value string
}
func validateIssueStrings(issue *types.Issue) error {
fields := []currentString{
{"issue id", issue.ID},
{"issue title", issue.Title},View on GitHub (pinned to 71377f2769)
Solutions
- Inspect the value: sqlite3 legacy.db "SELECT id, ephemeral, pinned, is_template FROM issues WHERE id='<id>'"
- Normalize the offending column to 0 or 1 (map any non-zero truthy to 1, otherwise 0), e.g. UPDATE issues SET ephemeral = CASE WHEN ephemeral = 0 THEN 0 ELSE 1 END WHERE id='<id>'
- Or set the column to NULL to leave the tri-state unset
- Re-run the migration
Example fix
// before ephemeral = -1 // after UPDATE issues SET ephemeral = 0 WHERE id = '<id>';
Defensive patterns
Strategy: validation
Validate before calling
-- run before migration SELECT id FROM issues WHERE (ephemeral IS NOT NULL AND ephemeral NOT IN (0,1)) OR (pinned IS NOT NULL AND pinned NOT IN (0,1)) OR (is_template IS NOT NULL AND is_template NOT IN (0,1));
Type guard
def tri_state_ok(v) -> bool:
return v is None or v in (0, 1) Prevention
- Treat tri-state booleans strictly as NULL/0/1 in all writers
- Normalize -1/other sentinels during any pre-migration ETL
- Add CHECK constraints to the legacy DB: CHECK (col IN (0,1)) or NULL
When it happens
Trigger: A legacy row stores a value other than 0, 1, or NULL in ephemeral, pinned, or is_template — commonly -1 used as a 'false' sentinel, or 2 from a bitfield-style writer.
Common situations: External scripts treating the columns as general integers; data imported from another tracker mapping booleans to non-0/1 integers; corruption.
Related errors
- legacy SQLite source %q must be a regular file
- --source-db and --output are required
- sealed legacy SQLite database does not match source fingerpr
- sealed legacy SQLite WAL does not match source fingerprint
- legacy SQLite source changed while sealing
AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30).
Data as JSON: /api/errors/23d6eacbcc455478.
Report an issue: GitHub.