{"record":{"id":"f8496cb8c586649f","repo":"gastownhall/beads","slug":"count-open-durable-children-for-s-w","errorCode":null,"errorMessage":"count open durable children for %s: %w","messagePattern":"count open durable children for (.+?): %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/storage/issueops/close.go","lineNumber":208,"sourceCode":"\treturn countOpenChildrenForTargetInTx(ctx, tx, id, targetColumn)\n}\n\nfunc countOpenChildrenForTargetInTx(ctx context.Context, tx DBTX, id, targetColumn string) (int, error) {\n\tif targetColumn != \"depends_on_issue_id\" && targetColumn != \"depends_on_wisp_id\" {\n\t\treturn 0, fmt.Errorf(\"count open children: unsupported target column %q\", targetColumn)\n\t}\n\tvar durableCount int\n\t//nolint:gosec // G201: targetColumn is validated above against two hardcoded identifiers.\n\tdurableQuery := fmt.Sprintf(`\n\t\tSELECT COUNT(DISTINCT dependency.issue_id)\n\t\tFROM dependencies AS dependency\n\t\tJOIN issues AS child ON child.id = dependency.issue_id\n\t\tWHERE dependency.%s = ?\n\t\t  AND dependency.type = 'parent-child'\n\t\t  AND child.status != 'closed'\n\t`, targetColumn)\n\tif err := tx.QueryRowContext(ctx, durableQuery, id).Scan(&durableCount); err != nil {\n\t\treturn 0, fmt.Errorf(\"count open durable children for %s: %w\", id, err)\n\t}\n\n\tvar wispCount int\n\t//nolint:gosec // G201: targetColumn is validated above against two hardcoded identifiers.\n\twispQuery := fmt.Sprintf(`\n\t\tSELECT COUNT(DISTINCT dependency.issue_id)\n\t\tFROM wisp_dependencies AS dependency\n\t\tJOIN wisps AS child ON child.id = dependency.issue_id\n\t\tWHERE dependency.%s = ?\n\t\t  AND dependency.type = 'parent-child'\n\t\t  AND child.status != 'closed'\n\t\t  AND NOT EXISTS (\n\t\t\tSELECT 1 FROM dependencies AS durable WHERE durable.id = dependency.id\n\t\t  )\n\t`, targetColumn)\n\tif err := tx.QueryRowContext(ctx, wispQuery, id).Scan(&wispCount); err != nil {\n\t\tif optionalBlockedTable(\"wisp_dependencies\") && isTableNotExistError(err) {\n\t\t\treturn durableCount, nil","sourceCodeStart":190,"sourceCodeEnd":226,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/internal/storage/issueops/close.go#L190-L226","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Inspect the wrapped driver error (%w) for the root cause — it is appended after this message","Retry the close if the cause was transient (connection blip, lock timeout); beads close is transactional and safe to retry","Verify schema integrity (bd doctor / migrations applied) if the error is 'table doesn't exist' for dependencies or issues","Check context deadlines if the error is context deadline exceeded — the dependency graph may be large or the DB slow"],"exampleFix":"// before\nctx := context.Background() // no deadline, hangs then aborts on server timeout\ncount, err := countOpenChildrenForTargetInTx(ctx, tx, id, col)\n// after\nctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)\ndefer cancel()\ncount, err := countOpenChildrenForTargetInTx(ctx, tx, id, col)","handlingStrategy":"retry","validationCode":"if err := db.PingContext(ctx); err != nil {\n\treturn fmt.Errorf(\"database unreachable before close: %w\", err)\n}","typeGuard":null,"tryCatchPattern":"count, err := countOpenChildrenForTargetInTx(ctx, tx, id, col)\nif err != nil {\n\tvar retryable bool\n\tif errors.Is(err, context.DeadlineExceeded) || dberrors.IsLockTimeout(err) || dberrors.IsConnectionError(err) {\n\t\tretryable = true\n\t}\n\tlog.Printf(\"child count failed for %s (retryable=%v): %v\", id, retryable, err)\n\tif retryable {\n\t\treturn retryClose(ctx, id)\n\t}\n\treturn err\n}","preventionTips":["Set explicit context timeouts sized for your dependency-graph size","Retry close operations on transient errors — they are transactional and idempotent","Keep the driver and Dolt/MySQL server versions current and migrations applied","Monitor connection pool health and lock-wait metrics in production","Avoid concurrent bulk dependency writes while closing issues"],"tags":["sql","database","dependency-tracking"],"backgroundTag":"sql-query-failed","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}