gastownhall/beads · error

db: DependencySQLRepository.Insert: record dependency_added

Error message

db: DependencySQLRepository.Insert: record dependency_added event: %w

What it means

This error wraps a failure from the events repository while recording the dependency_added event after a successful edge insert. The dependency was created, but writing its history entry to the source issue's event table failed, so the emit path aborts with this wrap.

Source

Thrown at internal/storage/domain/db/dependency.go:185

	// Record the dependency_added event on the source's event table, matching the
	// embedded/issueops AddDependencyInTx path so the bd CLI and library callers
	// observe the same history from either write plumbing. Reached only on the
	// genuine new-edge path; the idempotent same-type refresh returned earlier.
	// Gated on EmitEvent so only the explicit dep verbs emit: create-with-deps
	// and reparent call Insert directly without it, so an implicit parent-child /
	// --deps / waits-for edge produces no event. The embedded structural paths
	// (createIssueWithDeps, reparent) match this by calling the plain,
	// no-event AddDependency/tx.AddDependency, whose issueops.AddDependencyInTx
	// EmitEvent gate is likewise unset — so both backends stay silent on implicit
	// edges and emit only for the explicit bd dep add / bd link verbs.
	if opts.EmitEvent {
		if err := r.events.Record(ctx, domain.Event{
			IssueID:  dep.IssueID,
			Type:     types.EventDependencyAdded,
			Actor:    actor,
			NewValue: fmt.Sprintf("Added dependency: %s %s %s", dep.IssueID, dep.Type, dep.DependsOnID),
		}, domain.RecordEventOpts{UseWispsTable: opts.UseWispsTable}); err != nil {
			return fmt.Errorf("db: DependencySQLRepository.Insert: record dependency_added event: %w", err)
		}
	}

	// is_blocked maintenance mirrors the classic AddDependencyInTx flow
	// (issueops/dependencies.go): the affected set expands the source by its
	// parent-child descendants (plus, for parent-child edges, waiters on the
	// target spawner), then a Mark pass propagates blocked state — or, for
	// parent-child adds (not monotonic: an already-closed child can satisfy an
	// any-children waits-for gate), a full mark/unmark Recompute. Skipping the
	// expansion left descendants stale when a blocking edge landed on their
	// ancestor (bd-6dnrw.44 item 3).
	srcIsWisp := opts.UseWispsTable
	var affectedIssues, affectedWisps []string
	var aerr error
	if srcIsWisp {
		affectedIssues, affectedWisps, aerr = issueops.AffectedByDepChangeForWispInTx(ctx, r.runner, dep.IssueID, dep.DependsOnID, dep.Type)
	} else {
		affectedIssues, affectedWisps, aerr = issueops.AffectedByDepChangeInTx(ctx, r.runner, dep.IssueID, dep.DependsOnID, dep.Type)

View on GitHub (pinned to 71377f2769)

Solutions

  1. Check that the events table exists and matches the current schema (bd migrate).
  2. Verify write connectivity; retry the operation.
  3. If the event is non-essential for your flow, call Insert with EmitEvent=false to skip event recording.
  4. Inspect the wrapped driver error for the underlying cause.
Defensive patterns

Strategy: try-catch

Try / catch

if err := repo.Insert(ctx, dep, actor, opts); err != nil {
    if strings.Contains(err.Error(), "record dependency_added event") {
        // edge may exist; check before retrying; consider EmitEvent=false if audit not needed
    }
}

Prevention

When it happens

Trigger: Calling Insert with opts.EmitEvent=true where events.Record fails — event table missing, connection failure, or constraint error writing the Event row for dep.IssueID.

Common situations: Older database lacking the events table or expected columns; read-only replica receiving the write; DB connection dropped between dep insert and event record.

Related errors


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