gastownhall/beads · error

db: DependencySQLRepository.DetectCycles: %w

Error message

db: DependencySQLRepository.DetectCycles: %w

What it means

DetectCycles calls issueops.DetectCyclesInTx, which loads the dependency graph and finds dependency cycles. This wrapper means the underlying cycle-detection query/graph load failed; the repository adds only context. It is not a report that cycles were found — that is returned as data, not as an error.

Source

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

		if _, ok := allowed[d.DependencyType]; ok {
			out = append(out, d)
		}
	}
	return out
}

func (r *dependencySQLRepositoryImpl) IsBlocked(ctx context.Context, issueID string, opts domain.DepListOpts) (bool, []string, error) {
	blocked, blockers, err := issueops.IsBlockedInTx(ctx, r.runner, issueID)
	if err != nil {
		return false, nil, fmt.Errorf("db: DependencySQLRepository.IsBlocked %s: %w", issueID, err)
	}
	return blocked, blockers, nil
}

func (r *dependencySQLRepositoryImpl) DetectCycles(ctx context.Context) ([][]*types.Issue, error) {
	out, err := issueops.DetectCyclesInTx(ctx, r.runner)
	if err != nil {
		return nil, fmt.Errorf("db: DependencySQLRepository.DetectCycles: %w", err)
	}
	return out, nil
}

func (r *dependencySQLRepositoryImpl) DetectCycleReport(ctx context.Context) (publicops.CycleReport, error) {
	out, err := issueops.DetectCycleReportInTx(ctx, r.runner)
	if err != nil {
		return publicops.CycleReport{}, fmt.Errorf("db: DependencySQLRepository.DetectCycleReport: %w", err)
	}
	return out, nil
}

// WalkDependencyTree runs the SHARED walk body, unwrapped.
//
// It does NOT wrap the error the way its siblings above do, and that is the one
// thing to keep when editing it: the body publishes issueops.ErrValidation,
// storage.ErrNotFound and *issueops.ErrTooManyRows as the role's own vocabulary,
// and every one of those is classified by errors.Is/errors.As at both front

View on GitHub (pinned to 71377f2769)

Solutions

  1. Unwrap to find the root driver/SQL error.
  2. Ensure the database is running and schema is current (run migrations/doctor).
  3. Increase the context deadline for large dependency graphs.
  4. Retry after transient connectivity failures.

Example fix

// before
cycles, err := repo.DetectCycles(ctx) // ctx has no deadline control
// after
ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second)
defer cancel()
cycles, err := repo.DetectCycles(ctx)
Defensive patterns

Strategy: try-catch

Validate before calling

ctx, cancel := context.WithTimeout(ctx, 60*time.Second)
defer cancel()
if err := db.PingContext(ctx); err != nil {
    return fmt.Errorf("db unavailable before DetectCycles: %w", err)
}

Type guard

func isContextDeadline(err error) bool {
    return errors.Is(err, context.DeadlineExceeded)
}

Try / catch

cycles, err := repo.DetectCycles(ctx)
if err != nil {
    if errors.Is(err, context.DeadlineExceeded) {
        // retry with longer timeout or reduced scope
    }
    return fmt.Errorf("cycle detection: %w", err)
}

Prevention

When it happens

Trigger: Calling DetectCycles(ctx) when DetectCyclesInTx errors: failure reading the dependencies tables, connection loss, context cancellation, or driver error while building the graph.

Common situations: Running bd doctor or integrity checks against an unavailable database; schema mismatch (older database missing tables DetectCyclesInTx expects); long-running detection hitting a context deadline on large graphs.

Understand the failure class

Background: "query failed", "%w: SQL error" — wrapped database query errors in Go libraries explained — this error's family across 3 libraries.

Related errors


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