gastownhall/beads · error
delete: cascade expansion: %w
Error message
delete: cascade expansion: %w
What it means
deleteMany wraps errors from issueRepo.FindAllDependents when expanding the deletion set in cascade mode. The 'delete: cascade expansion:' prefix identifies the dependent-graph traversal as the failing step, with the repository error preserved via %w. Deletion is aborted before anything is removed.
Source
Thrown at internal/storage/domain/issue_delete.go:79
}
func (u *issueUseCaseImpl) PreviewDeleteWisp(ctx context.Context, ids []string) (DeletePreview, error) {
return u.previewDelete(ctx, ids)
}
func (u *issueUseCaseImpl) deleteMany(ctx context.Context, params DeleteIssuesParams, actor string) (DeleteIssuesResult, error) {
if len(params.IDs) == 0 {
return DeleteIssuesResult{}, nil
}
result := DeleteIssuesResult{}
allIDs := params.IDs
switch {
case params.Cascade:
expanded, err := u.issueRepo.FindAllDependents(ctx, params.IDs)
if err != nil {
return DeleteIssuesResult{}, fmt.Errorf("delete: cascade expansion: %w", err)
}
allIDs = expanded
case params.EnforceCascadePolicy:
// Embedded-parity dependent handling (see DeleteIssuesParams): without
// Cascade, an external dependent either blocks the delete (no Force) or
// is orphaned (Force), never silently swept.
externalBySource, err := u.externalDependents(ctx, params.IDs)
if err != nil {
return DeleteIssuesResult{}, err
}
if params.Force {
orphanSet := map[string]bool{}
for _, deps := range externalBySource {
for _, dep := range deps {
orphanSet[dep] = true
}
}
result.OrphanedIssues = sortedStringSet(orphanSet)View on GitHub (pinned to 71377f2769)
Solutions
- Unwrap the error chain to find the underlying FindAllDependents failure
- Confirm the Dolt database is reachable and healthy
- Retry the delete; cascade expansion is read-only and safe to re-run
- If schema errors persist, run beads schema migration/upgrade
Example fix
// before
res, err := uc.DeleteIssues(ctx, ids, actor)
if err != nil { return err }
// after
if err != nil {
if strings.Contains(err.Error(), "cascade expansion") {
// storage read failed; nothing was deleted — safe to retry
return retryDelete(ctx, ids)
}
return err
} Defensive patterns
Strategy: retry
Validate before calling
// pre-check storage before cascade delete
if err := pingDolt(ctx); err != nil {
return fmt.Errorf("cannot cascade delete now: %w", err)
} Try / catch
res, err := uc.DeleteIssues(ctx, ids, actor)
if err != nil {
if strings.Contains(err.Error(), "cascade expansion") {
// read-only phase failed; nothing deleted — safe to retry
return retryWithBackoff(func() error { _, err := uc.DeleteIssues(ctx, ids, actor); return err })
}
return err
} Prevention
- Confirm Dolt health before large cascade deletes
- Retry cascade deletes with exponential backoff
- Keep dependency-graph tables migrated to the current schema
When it happens
Trigger: Calling DeleteIssue/DeleteWisp/DeleteIssues/DeleteWisps with Cascade=true when FindAllDependents fails — DB connection failure, SQL error during the dependency-graph recursive query, or storage unavailable.
Common situations: Cascading deletes during large dependency graphs while Dolt restarts; schema drift breaking the dependents query; concurrent modifications locking rows during expansion.
Related errors
- remove dep %s -> %s: %w
- delete: partition: %w
- delete: count labels: %w
- delete: count events: %w
- delete: affected by deletion: %w
AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30).
Data as JSON: /api/errors/84a7857a35933f67.
Report an issue: GitHub.