gastownhall/beads · error

count open durable children for %s: %w

Error message

count open durable children for %s: %w

What it means

Beads throws this when the durable-side COUNT query — counting open parent-child dependencies via dependencies JOIN issues — fails at the SQL layer. The raw driver error is wrapped with the parent issue id so you can see which record the count was for. It aborts the close-policy check, so the close operation does not proceed.

Source

Thrown at internal/storage/issueops/close.go:208

	return countOpenChildrenForTargetInTx(ctx, tx, id, targetColumn)
}

func countOpenChildrenForTargetInTx(ctx context.Context, tx DBTX, id, targetColumn string) (int, error) {
	if targetColumn != "depends_on_issue_id" && targetColumn != "depends_on_wisp_id" {
		return 0, fmt.Errorf("count open children: unsupported target column %q", targetColumn)
	}
	var durableCount int
	//nolint:gosec // G201: targetColumn is validated above against two hardcoded identifiers.
	durableQuery := fmt.Sprintf(`
		SELECT COUNT(DISTINCT dependency.issue_id)
		FROM dependencies AS dependency
		JOIN issues AS child ON child.id = dependency.issue_id
		WHERE dependency.%s = ?
		  AND dependency.type = 'parent-child'
		  AND child.status != 'closed'
	`, targetColumn)
	if err := tx.QueryRowContext(ctx, durableQuery, id).Scan(&durableCount); err != nil {
		return 0, fmt.Errorf("count open durable children for %s: %w", id, err)
	}

	var wispCount int
	//nolint:gosec // G201: targetColumn is validated above against two hardcoded identifiers.
	wispQuery := fmt.Sprintf(`
		SELECT COUNT(DISTINCT dependency.issue_id)
		FROM wisp_dependencies AS dependency
		JOIN wisps AS child ON child.id = dependency.issue_id
		WHERE dependency.%s = ?
		  AND dependency.type = 'parent-child'
		  AND child.status != 'closed'
		  AND NOT EXISTS (
			SELECT 1 FROM dependencies AS durable WHERE durable.id = dependency.id
		  )
	`, targetColumn)
	if err := tx.QueryRowContext(ctx, wispQuery, id).Scan(&wispCount); err != nil {
		if optionalBlockedTable("wisp_dependencies") && isTableNotExistError(err) {
			return durableCount, nil

View on GitHub (pinned to 71377f2769)

Solutions

  1. Inspect the wrapped driver error (%w) for the root cause — it is appended after this message
  2. Retry the close if the cause was transient (connection blip, lock timeout); beads close is transactional and safe to retry
  3. Verify schema integrity (bd doctor / migrations applied) if the error is 'table doesn't exist' for dependencies or issues
  4. Check context deadlines if the error is context deadline exceeded — the dependency graph may be large or the DB slow

Example fix

// before
ctx := context.Background() // no deadline, hangs then aborts on server timeout
count, err := countOpenChildrenForTargetInTx(ctx, tx, id, col)
// after
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
count, err := countOpenChildrenForTargetInTx(ctx, tx, id, col)
Defensive patterns

Strategy: retry

Validate before calling

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

Try / catch

count, err := countOpenChildrenForTargetInTx(ctx, tx, id, col)
if err != nil {
	var retryable bool
	if errors.Is(err, context.DeadlineExceeded) || dberrors.IsLockTimeout(err) || dberrors.IsConnectionError(err) {
		retryable = true
	}
	log.Printf("child count failed for %s (retryable=%v): %v", id, retryable, err)
	if retryable {
		return retryClose(ctx, id)
	}
	return err
}

Prevention

When it happens

Trigger: Calling CloseIssue (or EnforceClosePolicyInTx / CloseIssueCheckedInTx) when the query 'SELECT COUNT(DISTINCT dependency.issue_id) FROM dependencies JOIN issues ... WHERE dependency.<col> = ?' returns a driver error: connection failure, lock wait timeout, corrupt schema, missing dependencies/issues table, or context cancellation/deadline exceeded mid-query.

Common situations: Database server restarted or network dropped mid-transaction; Dolt/MySQL error 1146 because the dependencies table is missing in a partially-migrated database; lock contention from another process holding rows in dependencies; context timeout on a slow, large dependency graph.

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