gastownhall/beads · error

db: DependencySQLRepository.DetectCycleReport: %w

Error message

db: DependencySQLRepository.DetectCycleReport: %w

What it means

DetectCycleReport calls issueops.DetectCycleReportInTx and wraps its failure with repository context. DetectCycleReportInTx builds a richer cycle report than DetectCycles; failure here means the underlying report queries/graph load errored, not that cycles exist (cycles are reported in the returned CycleReport value).

Source

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

	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
// doors and in the HTTP problem mapping. A `fmt.Errorf("db: ...: %w")` would keep
// them matchable but would also put this repository's name into the message a
// user reads, which the direct route never does for the same refusal.
func (r *dependencySQLRepositoryImpl) WalkDependencyTree(ctx context.Context, req publicops.WalkTreeRequest) (publicops.TreeResult, error) {
	return issueops.WalkDependencyTreeInTx(ctx, r.runner, req)
}

// CountEdges runs the SHARED edge-count body, unwrapped for

View on GitHub (pinned to 71377f2769)

Solutions

  1. Unwrap the error to expose the root cause from DetectCycleReportInTx.
  2. Verify database availability and schema migrations.
  3. Retry with a longer context timeout.
  4. Fall back to DetectCycles if only cycle membership is needed.

Example fix

// before
report, err := repo.DetectCycleReport(ctx)
// after: tolerate failure and fall back
report, err := repo.DetectCycleReport(ctx)
if err != nil {
    cycles, cerr := repo.DetectCycles(ctx)
    if cerr != nil { return fmt.Errorf("cycle detection failed: %w", err) }
}
Defensive patterns

Strategy: fallback

Validate before calling

if err := db.PingContext(ctx); err != nil {
    return fmt.Errorf("db unavailable before DetectCycleReport: %w", err)
}

Type guard

func isTransientDBErr(err error) bool {
    return errors.Is(err, driver.ErrBadConn) || errors.Is(err, context.DeadlineExceeded)
}

Try / catch

report, err := repo.DetectCycleReport(ctx)
if err != nil {
    // fall back to plain cycle detection
    cycles, cerr := repo.DetectCycles(ctx)
    if cerr != nil { return fmt.Errorf("cycle report: %w", err) }
    _ = cycles
}

Prevention

When it happens

Trigger: Calling DetectCycleReport(ctx) when the underlying report generation fails: SQL errors reading dependency data, connection drops, or context cancellation inside DetectCycleReportInTx.

Common situations: Integrity/doctor reports against a stopped or corrupted Dolt database; schema drift after a version upgrade; timeouts on very large dependency sets.

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/4d4115f6c30a5413. Report an issue: GitHub.