gastownhall/beads · error
remove dep: classify source: %w
Error message
remove dep: classify source: %w
What it means
Wraps a failure of depRepo.WispSourceIDs while RemoveDependencyBySource classifies whether the edge's source issue lives in the wisps table or the regular table. The classification determines which table the delete targets; when the lookup itself fails, the removal is aborted with this wrapper so the caller knows the failure happened before any delete was attempted.
Source
Thrown at internal/storage/domain/dependency.go:352
return u.removeDep(ctx, wispID, dependsOnID, actor, true)
}
// RemoveDependencyBySource removes one edge from the plane its SOURCE lives in
// and reports whether there was an edge to remove.
//
// It is the source-routed twin of AddDependencies, and exists for the same
// reason: `bd dep remove` takes whatever id the caller names, and pinning the
// removal to the durable table means failing to remove an edge whose source is
// a wisp while reporting that it was never there (bd-yby99.17). The delete IS
// the verdict, the way the store-backed body reads it off RemoveDependencyInTx
// rather than from a separate lookup.
func (u *dependencyUseCaseImpl) RemoveDependencyBySource(ctx context.Context, sourceID, dependsOnID, actor string) (bool, error) {
if sourceID == "" || dependsOnID == "" {
return false, fmt.Errorf("remove dep: sourceID and dependsOnID must not be empty")
}
wispSources, err := u.depRepo.WispSourceIDs(ctx, []string{sourceID})
if err != nil {
return false, fmt.Errorf("remove dep: classify source: %w", err)
}
_, sourceIsWisp := wispSources[sourceID]
res, err := u.depRepo.Delete(ctx, sourceID, dependsOnID, actor, DepInsertOpts{UseWispsTable: sourceIsWisp, EmitEvent: true})
if err != nil {
return false, fmt.Errorf("remove dep %s -> %s: %w", sourceID, dependsOnID, err)
}
return res.Found, nil
}
func (u *dependencyUseCaseImpl) removeDep(ctx context.Context, sourceID, dependsOnID, actor string, useWisp bool) error {
if sourceID == "" || dependsOnID == "" {
return fmt.Errorf("remove dep: sourceID and dependsOnID must not be empty")
}
if _, err := u.depRepo.Delete(ctx, sourceID, dependsOnID, actor, DepInsertOpts{UseWispsTable: useWisp, EmitEvent: true}); err != nil {
return fmt.Errorf("remove dep %s -> %s: %w", sourceID, dependsOnID, err)
}
return nil
}View on GitHub (pinned to 71377f2769)
Solutions
- Fix the underlying repository error surfaced by %w (connectivity, SQL, context).
- Confirm the database schema is current (bd doctor / migration status) since wisp classification reads the wisps table.
- Retry the removal once storage is healthy — no delete has run yet, so it is safe to re-invoke.
- If timeouts are the cause, increase the context deadline for large batch scripts.
Example fix
// before: assuming removal failed because the edge was missing
removed, err := uc.RemoveDependencyBySource(ctx, src, dst, actor)
if err != nil {
log.Println("edge not found")
}
// after: distinguish classification failure from not-found
removed, err := uc.RemoveDependencyBySource(ctx, src, dst, actor)
if err != nil {
log.Printf("removal aborted before delete: %v", err) // storage issue
} Defensive patterns
Strategy: try-catch
Validate before calling
// Pre-verify storage reachability before batch removal
if err := db.PingContext(ctx); err != nil {
return fmt.Errorf("storage unreachable, aborting removals: %w", err)
} Try / catch
_, err := uc.RemoveDependencyBySource(ctx, src, dst, actor)
if err != nil {
if errors.Is(err, context.DeadlineExceeded) || errors.Is(err, context.Canceled) {
return retryWithLongerDeadline()
}
return fmt.Errorf("classification/read failure, nothing deleted: %w", err)
} Prevention
- Use contexts with sufficient deadlines for batch scripts
- Confirm schema/migrations are current so the wisps table is queryable
- Retry safely: a classification failure means no delete occurred
- Monitor DB connectivity before running large dep-removal jobs
When it happens
Trigger: RemoveDependencyBySource calls WispSourceIDs(ctx, []string{sourceID}) and the repo query fails — DB unreachable, SQL error, context canceled, or Dolt transaction/lock error while scanning the wisps classification.
Common situations: Database connection dropped mid-command; context deadline exceeded during batch removals; Dolt server error or schema mismatch after a version upgrade; read-only replica used for the classification query.
Related errors
- determining wisp status for %s: %w
- loading proto: %w
- failed to close wisp root %s: %w
- failed to clear ephemeral flag on root %s: %w
- add dep: cycle check: %w
AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30).
Data as JSON: /api/errors/e4dcd403d4e6417a.
Report an issue: GitHub.