gastownhall/beads · error
delete: count deps: %w
Error message
delete: count deps: %w
What it means
Wraps a failure from depRepo.ListByIssueIDs in countDeletedDependencies, which counts dependency edges that a deleteMany will remove across both the durable and wisp dependency planes. A table-not-exist error on the wisp pass is tolerated; this error fires for any other failure, including failures on the durable (first) pass.
Source
Thrown at internal/storage/domain/issue_delete.go:480
// (batch)` was run per 50-id batch, so an edge whose two ends fell in
// different batches matched twice. Keying by the edge itself removes that
// hazard rather than trading it for another: a row is counted once whether it
// is reached as somebody's outbound edge, somebody's inbound edge, or both.
func (u *issueUseCaseImpl) countDeletedDependencies(ctx context.Context, allIDs []string) (int, error) {
if len(allIDs) == 0 {
return 0, nil
}
seen := make(map[string]bool)
for _, useWisps := range []bool{false, true} {
edges, err := u.depRepo.ListByIssueIDs(ctx, allIDs, DepListOpts{
Direction: DepDirectionBoth,
UseWispsTable: useWisps,
})
if err != nil {
if useWisps && dberrors.IsTableNotExist(err) {
continue
}
return 0, fmt.Errorf("delete: count deps: %w", err)
}
for _, side := range []map[string][]*types.Dependency{edges.Outgoing, edges.Incoming} {
for _, list := range side {
for _, dep := range list {
if dep == nil {
continue
}
// (source, target) is unique per table: the writer refuses a
// second edge for a pair, retyping included.
seen[fmt.Sprintf("%t\x00%s\x00%s", useWisps, dep.IssueID, dep.DependsOnID)] = true
}
}
}
}
return len(seen), nil
}
View on GitHub (pinned to 71377f2769)
Solutions
- Inspect the wrapped cause and fix the underlying storage issue (lock, connectivity, corruption).
- Retry the delete when no other process is writing to the store.
- Use a fresh, non-canceled context for the operation.
- Run bd doctor to check database health if the error persists.
- Verify the dependencies (and wisp_dependencies) tables exist and match your driver's expected schema.
Defensive patterns
Strategy: try-catch
Validate before calling
// pre-check dependency tables are readable before delete
if _, err := usecase.GetDependencies(ctx, firstID); err != nil {
return fmt.Errorf("dependency store unreadable: %w", err)
} Type guard
func isCountDepsErr(err error) bool {
return err != nil && strings.Contains(err.Error(), "delete: count deps:")
} Try / catch
err := usecase.DeleteMany(ctx, ids)
if err != nil && strings.Contains(err.Error(), "delete: count deps:") {
cause := errors.Unwrap(err)
if !dberrors.IsTableNotExist(cause) {
// retry with backoff or surface a storage-health problem
}
} Prevention
- Avoid deleting during Dolt maintenance (GC/compaction) windows
- Limit parallel bd commands against one store to prevent pool exhaustion
- Retry transient storage errors with backoff before aborting
- Verify dependency-table schema matches your driver version after upgrades
When it happens
Trigger: deleteMany of issues that have dependency edges, where ListByIssueIDs fails on either plane — locked database, dropped connection, context cancellation, or a driver-level SQL error.
Common situations: Deleting from a store while a Dolt compaction/GC runs; connection pool exhaustion under many parallel bd commands; canceled deletes; a dependency table corrupted or locked by another transaction.
Understand the failure class
Background: Database query failed: Internal Server Error 500s wrapping SQL, Prisma, and connection failures — what to check first — this error's family across 16 libraries.
Related errors
- delete: drop deps: %w
- delete: drop wisp deps: %w
- failed to remove relates-to %s -> %s: %w
- failed to get dependencies for %s: %w
- add deps: classify sources: %w
AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30).
Data as JSON: /api/errors/94bc7d01cc69f20b.
Report an issue: GitHub.