gastownhall/beads · error
dependency %s -> %s uses unsupported metadata or thread ID
Error message
dependency %s -> %s uses unsupported metadata or thread ID
What it means
The current bd dependency model does not support per-dependency metadata or thread IDs, so the legacy importer refuses to bring in rows that carry them. Any dependency whose metadata or thread_id column is non-empty (and valid) aborts migration for that database.
Source
Thrown at internal/migration/legacysqlite/reader.go:923
return fmt.Errorf("orphan dependency %s -> %s", id, to)
}
if issue.Ephemeral != byID[to].Ephemeral {
return fmt.Errorf("dependency %s -> %s crosses ephemeral storage", id, to)
}
key := id + "\x00" + to
if seenDeps[key] {
return fmt.Errorf("multiple legacy dependencies for %s -> %s", id, to)
}
seenDeps[key] = true
created, e := parseTime(at)
if e != nil {
return e
}
if created.IsZero() {
return fmt.Errorf("dependency created_at is zero for %s -> %s", id, to)
}
if (metadata.Valid && metadata.String != "") || (thread.Valid && thread.String != "") {
return fmt.Errorf("dependency %s -> %s uses unsupported metadata or thread ID", id, to)
}
if !types.DependencyType(typ).IsValid() {
return fmt.Errorf("dependency %s -> %s has invalid type", id, to)
}
d := &types.Dependency{IssueID: id, DependsOnID: to, Type: types.DependencyType(typ), CreatedAt: created, CreatedBy: by, Metadata: nullString(metadata), ThreadID: nullString(thread)}
issue.Dependencies = append(issue.Dependencies, d)
return nil
}
func loadComments(ctx context.Context, db *sql.Tx, byID map[string]*types.Issue) error {
comments, err := db.QueryContext(ctx, "SELECT id,issue_id,author,text,CAST(created_at AS TEXT) FROM comments ORDER BY issue_id,created_at,id")
if err != nil {
return err
}
defer comments.Close()
type commentIdentity struct {
issueID, author, text string
createdAt time.TimeView on GitHub (pinned to 71377f2769)
Solutions
- Clear the unsupported columns in the legacy DB: UPDATE dependencies SET metadata = NULL, thread_id = NULL where they are non-empty
- If the metadata/thread info matters, record it elsewhere (issue notes) before clearing
- Migrate with a reader version that matches the legacy schema, or drop the rows entirely
Example fix
-- before
-- dependency row has metadata='{"priority":1}'
UPDATE dependencies SET metadata = NULL, thread_id = NULL;
-- after: importer accepts the row Defensive patterns
Strategy: validation
Validate before calling
for (const d of legacyDeps) {
if ((d.metadata && d.metadata !== '') || (d.thread_id && d.thread_id !== ''))
throw new Error(`unsupported metadata/thread on ${d.issue_id} -> ${d.depends_on_id}`);
} Type guard
function isPlainDependency(d) {
return (!d.metadata || d.metadata === '') && (!d.thread_id || d.thread_id === '');
} Try / catch
try { migrateLegacySQLite(dbPath) } catch (e) {
if (e.message.includes('unsupported metadata or thread')) {
clearDepMetadataColumns(dbPath); // SET metadata=NULL, thread_id=NULL
retry();
} else throw e;
} Prevention
- NULL out metadata/thread_id on dependency rows before migrating
- Record any needed metadata in issue notes first — the current model drops it
- Keep the legacy DB schema aligned with the reader version you migrate with
When it happens
Trigger: Legacy SQLite dependency rows with non-empty metadata and/or thread_id columns; the reader checks `metadata.Valid && metadata.String != ""` and likewise for thread before appending the Dependency.
Common situations: Legacy databases produced by experimental/newer builds that wrote dependency metadata or thread IDs; manually edited rows; schema drift between legacy versions.
Related errors
- 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
- legacy SQLite source %q must be a regular file
AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30).
Data as JSON: /api/errors/faf8497b13927f18.
Report an issue: GitHub.