gastownhall/beads · error
legacy SQLite ephemeral comment text is %d bytes (current TE
Error message
legacy SQLite ephemeral comment text is %d bytes (current TEXT maximum %d)
What it means
When migrating a comment that belongs to an ephemeral (short-lived) issue, the reader enforces the current TEXT byte maximum: if the comment text exceeds currentTextBytes the import aborts, because the current storage cannot hold it.
Source
Thrown at internal/migration/legacysqlite/reader.go:968
if err := checkUTF8(
currentString{"comment issue_id", issueID},
currentString{"comment author", author},
currentString{"comment text", text},
); err != nil {
return err
}
if err := checkCurrentVarchars(
currentVarchar{"comment issue_id", issueID, types.MaxFieldLen},
currentVarchar{"comment author", author, types.MaxFieldLen},
); err != nil {
return err
}
issue := byID[issueID]
if issue == nil {
return fmt.Errorf("orphan comment for %s", issueID)
}
if issue.Ephemeral && len(text) > currentTextBytes {
return fmt.Errorf("legacy SQLite ephemeral comment text is %d bytes (current TEXT maximum %d)", len(text), currentTextBytes)
}
created, e := parseTime(at)
if e != nil {
return e
}
if created.IsZero() {
return fmt.Errorf("comment created_at is zero for issue %s", issueID)
}
identity := commentIdentity{issueID: issueID, author: author, text: text, createdAt: created}
if priorID, exists := seenComments[identity]; exists {
return fmt.Errorf("legacy SQLite comments %d and %d share current import identity", priorID, id)
}
seenComments[identity] = id
issue.Comments = append(issue.Comments, &types.Comment{ID: strconv.FormatInt(id, 10), IssueID: issueID, Author: author, Text: text, CreatedAt: created})
}
return comments.Err()
}
View on GitHub (pinned to 71377f2769)
Solutions
- Trim or split the oversized comment text in the legacy DB so it fits within the current byte maximum
- Reclassify the parent issue as non-ephemeral in the legacy DB if the large comment is essential
- Archive the full text outside bd and insert a shortened placeholder comment before migrating
Example fix
-- before -- comment text length 12000 bytes > ephemeral max 8000 UPDATE comments SET text = substr(text, 1, 8000) WHERE length(cast(text as blob)) > 8000 AND issue_id IN (SELECT id FROM issues WHERE ephemeral = 1); -- after: comment fits the ephemeral TEXT maximum
Defensive patterns
Strategy: validation
Validate before calling
const MAX = currentTextBytes;
for (const c of legacyComments.filter(c => ephemeralIssueIds.has(c.issue_id))) {
if (Buffer.byteLength(c.text, 'utf8') > MAX)
throw new Error(`ephemeral comment too large on ${c.issue_id}: ${Buffer.byteLength(c.text)} > ${MAX}`);
} Type guard
function fitsEphemeralLimit(c) {
return !ephemeralIssueIds.has(c.issue_id) || Buffer.byteLength(c.text, 'utf8') <= currentTextBytes;
} Try / catch
try { migrateLegacySQLite(dbPath) } catch (e) {
if (e.message.includes('ephemeral comment text is')) {
truncateOversizedEphemeralComments(dbPath);
retry();
} else throw e;
} Prevention
- Trim or archive oversized comments on ephemeral issues before migrating
- Count UTF-8 bytes, not characters, when checking sizes
- Re-run the size check after any tooling change that edits comment text
When it happens
Trigger: Legacy SQLite comment rows attached to an issue where issue.Ephemeral is true and len(text) exceeds the current ephemeral TEXT limit in bytes (multi-byte UTF-8 counts per byte, not per rune).
Common situations: Long log excerpts or pasted output stored as comments on ephemeral issues in old databases; limits tightened since the legacy DB was written so previously-legal comments now exceed the cap.
Related errors
- legacy SQLite issue payload is %d bytes (current TEXT maximu
- legacy SQLite issue waiters serialize to %d bytes (current T
- 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/ceecf05d21ec09b1.
Report an issue: GitHub.