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.Time

View on GitHub (pinned to 71377f2769)

Solutions

  1. Clear the unsupported columns in the legacy DB: UPDATE dependencies SET metadata = NULL, thread_id = NULL where they are non-empty
  2. If the metadata/thread info matters, record it elsewhere (issue notes) before clearing
  3. 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

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


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