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

  1. Inspect the value: sqlite3 legacy.db "SELECT id, ephemeral, pinned, is_template FROM issues WHERE id='<id>'"
  2. 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>'
  3. Or set the column to NULL to leave the tri-state unset
  4. 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

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


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