gastownhall/beads · error
query neighbors from %s: %w
Error message
query neighbors from %s: %w
What it means
deleteNeighborsInTx queries both dependency planes for surviving rows joined to the deletion set in either direction, so their text can later be rewritten with [deleted:<id>] markers. This error wraps a query failure and names the table. Absent optional tables (wisp_dependencies) are skipped via optionalBlockedTable + isTableNotExistError, so reaching this error means a real failure on a table that exists.
Source
Thrown at internal/storage/issueops/delete_role.go:272
neighborIDs := make(map[string]bool)
for i := 0; i < len(ids); i += deleteBatchSize {
end := i + deleteBatchSize
if end > len(ids) {
end = len(ids)
}
inClause, args := buildSQLInClause(ids[i:end])
doubled := append(append([]interface{}{}, args...), args...)
for _, depTable := range []string{"dependencies", "wisp_dependencies"} {
rows, err := tx.QueryContext(ctx,
fmt.Sprintf(`SELECT issue_id, %s AS depends_on_id FROM %s WHERE issue_id IN (%s) OR %s`,
DepTargetExpr, depTable, inClause, depTargetIn("", inClause)),
doubled...)
if err != nil {
if optionalBlockedTable(depTable) && isTableNotExistError(err) {
continue
}
return nil, fmt.Errorf("query neighbors from %s: %w", depTable, err)
}
for rows.Next() {
var source, target string
if err := rows.Scan(&source, &target); err != nil {
_ = rows.Close()
return nil, fmt.Errorf("scan neighbor: %w", err)
}
for _, candidate := range [2]string{source, target} {
if candidate == "" || deleting[candidate] {
continue
}
neighborIDs[candidate] = true
}
}
_ = rows.Close()
if err := rows.Err(); err != nil {
return nil, fmt.Errorf("iterate neighbors from %s: %w", depTable, err)
}View on GitHub (pinned to 71377f2769)
Solutions
- Read the wrapped driver error and the named table in the message
- Check connectivity and retry; the transaction rolls back so the delete is safely re-runnable
- Raise the context timeout for large cascade batches
- Confirm migrations for both dependency tables are applied
- Verify driver version matches what isTableNotExistError/optionalBlockedTable expect
Defensive patterns
Strategy: try-catch
Validate before calling
// ensure the backend is reachable before a wide delete
if err := db.PingContext(ctx); err != nil { return err } Try / catch
err := store.Delete(ctx, req)
if err != nil && strings.Contains(err.Error(), "query neighbors from") {
// driver/query failure on a dependency plane; inspect unwrapped cause, retry
} Prevention
- Keep the delete transaction short; check connection health beforehand
- Apply all migrations for both dependency planes
- Size context timeouts to the cascade breadth
- Verify driver versions after upgrades
When it happens
Trigger: deleteNeighborsInTx (called from DeleteInTx with set.All) runs tx.QueryContext with the doubled-args IN query on 'dependencies' or 'wisp_dependencies' and gets a non-'table not exist' error — connection failure, driver error, or cancelled context.
Common situations: Connection lost between deletion-set resolution and the neighborhood read; context timeout on a large cascade set; driver mismatch on the DepTargetExpr expression; missing migration.
Related errors
- query dependents from %s: %w
- compare local HEAD to %s: %w
- failed to begin transaction: %w
- failed to recompute is_blocked: %w
- failed to commit is_blocked repairs: %w
AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30).
Data as JSON: /api/errors/9c32cfb6de79abc6.
Report an issue: GitHub.