gastownhall/beads · error

count open wisp children for %s: %w

Error message

count open wisp children for %s: %w

What it means

Beads throws this when the wisp-side child count query — counting open parent-child dependencies in the optional wisp_dependencies table joined with wisps — fails with a driver error other than 'table does not exist'. If the wisp tables simply don't exist (optional feature) the count gracefully degrades to the durable count; any other SQL failure is wrapped with the parent id and aborts the close-policy check.

Source

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

	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
		}
		return 0, fmt.Errorf("count open wisp children for %s: %w", id, err)
	}
	return durableCount + wispCount, nil
}

func createCloseCheckedSavepoint(ctx context.Context, tx DBTX) (string, error) {
	name := closeCheckedSavepointPrefix + strconv.FormatUint(closeCheckedSavepointCounter.Add(1), 10)
	//nolint:gosec // G201: name is a fixed identifier-safe prefix plus an atomic decimal counter.
	if _, err := tx.ExecContext(ctx, "SAVEPOINT "+name); err != nil {
		return "", fmt.Errorf("create checked close savepoint: %w", err)
	}
	return name, nil
}

func rollbackAndReleaseCloseCheckedSavepoint(ctx context.Context, tx DBTX, name string) error {
	rollbackErr := rollbackToCloseCheckedSavepoint(ctx, tx, name)
	releaseErr := releaseCloseCheckedSavepoint(ctx, tx, name)
	return errors.Join(rollbackErr, releaseErr)
}

View on GitHub (pinned to 71377f2769)

Solutions

  1. Read the wrapped driver error after 'count open wisp children for <id>:' for the true cause
  2. Retry the close if transient (connection, lock timeout)
  3. Run schema verification/migrations — wisp tables and columns must both exist and match the expected shape
  4. If wisps are unused in your deployment, ensure the optional-table gating (sqlbuild.OptionalWispTable) is configured so missing tables are skipped rather than half-present
Defensive patterns

Strategy: try-catch

Validate before calling

// Verify wisp schema is complete if you use wisps
var one int
if err := db.QueryRow("SELECT 1 FROM wisp_dependencies LIMIT 1").Scan(&one); err != nil && !dberrors.IsTableNotExist(err) {
	return fmt.Errorf("wisp_dependencies unreadable: %w", err)
}
if err := db.QueryRow("SELECT 1 FROM wisps LIMIT 1").Scan(&one); err != nil && !dberrors.IsTableNotExist(err) {
	return fmt.Errorf("wisps unreadable: %w", err)
}

Try / catch

count, err := countOpenChildrenForTargetInTx(ctx, tx, id, col)
if err != nil {
	if dberrors.IsTableNotExist(err) {
		log.Printf("wisp schema missing for %s; proceeding with durable count only", id)
	} else {
		return fmt.Errorf("close of %s blocked: %w", id, err)
	}
}

Prevention

When it happens

Trigger: Calling CloseIssue / EnforceClosePolicyInTx when wisp_dependencies exists but the query fails: connection error, lock timeout, context cancellation, permission denied, or a malformed/corrupt wisp_dependencies table (e.g. missing the NOT EXISTS-referenced dependencies.id column).

Common situations: Database became unavailable between the durable and wisp queries; wisp schema partially created (wisp_dependencies exists but wisps missing or vice versa); migration created wisp tables without needed columns; lock contention on wisp_dependencies.

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