gastownhall/beads · error
legacy SQLite comments %d and %d share current import identi
Error message
legacy SQLite comments %d and %d share current import identity
What it means
The reader builds an import identity for each comment from (issueID, author, text, createdAt) and tracks previously seen identities. Two legacy comments with identical identity would collide in the current model (which keys comments on these fields), so migration aborts naming both legacy row IDs.
Source
Thrown at internal/migration/legacysqlite/reader.go:979
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()
}
func validateDependencyGraph(issues []*types.Issue) error {
scheduling := make(map[string][]string)
hierarchy := make(map[string][]string)
var blocking []*types.Dependency
for _, issue := range issues {
for _, dep := range issue.Dependencies {
if dep.IssueID == dep.DependsOnID {
return fmt.Errorf("dependency %s -> %s is a self-dependency", dep.IssueID, dep.DependsOnID)
}
switch dep.Type {
case types.DepBlocks, types.DepConditionalBlocks:View on GitHub (pinned to 71377f2769)
Solutions
- Deduplicate the comments table keeping the lowest id: DELETE FROM comments WHERE id NOT IN (SELECT MIN(id) FROM comments GROUP BY issue_id, author, text, created_at)
- If the duplicates should both survive, alter one copy's text or created_at so identities differ
- Verify with SELECT issue_id, author, text, created_at, COUNT(*) FROM comments GROUP BY 1,2,3,4 HAVING COUNT(*) > 1 before re-running migration
Example fix
-- before -- comments 42 and 57 are identical (issue, author, text, created_at) DELETE FROM comments WHERE id = 57; -- keep 42 -- after: unique comment identity per issue/author/text/time
Defensive patterns
Strategy: validation
Validate before calling
const seen = new Set();
for (const c of legacyComments) {
const k = `${c.issue_id}|${c.author}|${c.text}|${c.created_at}`;
if (seen.has(k)) throw new Error(`duplicate comment identity: ${k}`);
seen.add(k);
} Type guard
function commentsHaveUniqueIdentity(comments) {
const keys = comments.map(c => `${c.issue_id}|${c.author}|${c.text}|${c.created_at}`);
return new Set(keys).size === keys.length;
} Try / catch
try { migrateLegacySQLite(dbPath) } catch (e) {
if (e.message.includes('share current import identity')) {
dedupeCommentsKeepLowestId(dbPath); // GROUP BY issue_id, author, text, created_at
retry();
} else throw e;
} Prevention
- Deduplicate comments with GROUP BY issue_id, author, text, created_at before migrating
- Guard legacy comment-insert scripts against re-runs
- Restore legacy DBs from a single consistent backup, not overlapping copies
When it happens
Trigger: Legacy SQLite comments table containing two rows with the same issue_id, author, text, and created_at (differing only in their internal row id); the reader reports "comments N and M share current import identity".
Common situations: Duplicate inserts from re-run sync scripts; legacy DB restores that doubled comment rows; old tools that appended comments without dedup checks.
Related errors
- multiple legacy dependencies for %s -> %s
- sealed legacy SQLite database does not match source fingerpr
- sealed legacy SQLite WAL does not match source fingerprint
- legacy SQLite source changed while sealing
- legacy SQLite source %q must not be a symlink
AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30).
Data as JSON: /api/errors/d850fc2bbc06b682.
Report an issue: GitHub.