gastownhall/beads · error
failed to check for dependency cycle: %w
Error message
failed to check for dependency cycle: %w
What it means
Wraps an infrastructure error from WouldCreateSchedulingCycleInTx while CheckDependencyCycleInTx is determining whether the new edge would form a cycle. This is not a detected cycle (that returns domain.ErrDependencyCycle); it means the cycle-detection queries themselves failed, so the check could not be performed.
Source
Thrown at internal/storage/issueops/dependencies.go:450
)
`, sourceTable, targetTable), source, target)
return err
}
// CheckDependencyCycleInTx rejects self-dependencies and cycles across the
// combined blocks, conditional-blocks, and parent-child graph before insert.
// The caller may pass a restricted depTables list for a known storage bucket;
// nil uses all dependency tables.
func CheckDependencyCycleInTx(ctx context.Context, tx DBTX, dep *types.Dependency, depTables []string) error {
if dep.IssueID == dep.DependsOnID {
return fmt.Errorf("%w: %s cannot depend on itself", domain.ErrSelfDependency, dep.IssueID)
}
if !types.IsSchedulingEdge(dep.Type) {
return nil
}
wouldCycle, err := WouldCreateSchedulingCycleInTx(ctx, tx, dep.IssueID, dep.DependsOnID, depTables)
if err != nil {
return fmt.Errorf("failed to check for dependency cycle: %w", err)
}
if wouldCycle {
return domain.ErrDependencyCycle
}
return nil
}
// WouldCreateSchedulingCycleInTx reports whether adding issueID -> dependsOnID
// would close a cycle in the combined scheduling graph. It is shared by the
// classic and domain storage stacks so both traverse the same dependency types
// and typed target columns.
func WouldCreateSchedulingCycleInTx(ctx context.Context, tx DBTX, issueID, dependsOnID string, depTables []string) (bool, error) {
if len(depTables) == 0 {
depTables = cycleDetectionTables()
}
var reachable int
query := cycleReachabilityQuery(depTables)
if err := tx.QueryRowContext(ctx, query, dependsOnID, issueID).Scan(&reachable); err != nil {View on GitHub (pinned to 71377f2769)
Solutions
- Inspect the wrapped cause (%w) for the real SQL failure and fix that
- Verify the depTables argument names valid storage buckets (or pass nil for all tables)
- Retry once the database is healthy; the check is read-only so a retry is safe
- Repair dangling dependency rows if traversal errors point at missing referenced issues
Example fix
// before
if err := issueops.CheckDependencyCycleInTx(ctx, tx, dep, []string{"dep_links"}); err != nil {
return err
}
// after: use the default tables (nil = all dependency tables)
if err := issueops.CheckDependencyCycleInTx(ctx, tx, dep, nil); err != nil {
if errors.Is(err, domain.ErrDependencyCycle) {
return err
}
return fmt.Errorf("cycle check infra failure: %w", err)
} Defensive patterns
Strategy: retry
Validate before calling
// check both endpoints exist and tables are valid before cycle check
if !exists(ctx, db, dep.IssueID) || !exists(ctx, db, dep.DependsOnID) {
return errors.New("dependency endpoint missing")
}
if depTables != nil {
for _, t := range depTables {
if !validDepTable(t) { return fmt.Errorf("unknown dep table %q", t) }
}
} Type guard
func isCycleCheckInfraFailure(err error) bool {
return err != nil &&
!errors.Is(err, domain.ErrDependencyCycle) &&
strings.Contains(err.Error(), "failed to check for dependency cycle")
} Try / catch
err := issueops.CheckDependencyCycleInTx(ctx, tx, dep, nil)
switch {
case errors.Is(err, domain.ErrDependencyCycle):
return err // genuine cycle
case err != nil:
return retryWithBackoff(op) // infra failure, read-only check is safe to retry
} Prevention
- Pass nil depTables unless you know the bucket, to avoid invalid table lists
- Treat this wrap as retryable — it is never a real cycle result
- Monitor DB health; this surfaces transient query failures
- Repair dangling dependency rows that can break traversal
When it happens
Trigger: CheckDependencyCycleInTx called with a scheduling-edge dependency where the underlying graph traversal queries in WouldCreateSchedulingCycleInTx error out — DB unreachable, transaction aborted, invalid depTables list, or corrupt rows in the dependency tables.
Common situations: Transient database failures during a dep-add; wrong depTables argument filtering to a nonexistent bucket; migration drift leaving dependency rows referencing missing issues.
Related errors
- final cycle check failed (no edges added): %w
- ErrTransaction
- open unit of work: %w
- failed to begin transaction: %w
- failed to commit is_blocked repairs: %w
AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30).
Data as JSON: /api/errors/bc952b3626a1ade9.
Report an issue: GitHub.