gastownhall/beads · error

journal: query dependency removals from %s: %w

Error message

journal: query dependency removals from %s: %w

What it means

dependencyEdgesInTableForIssueIDsInTx queries one dependency table (per `table`) for edges touching the given issue IDs, to record removal events. This error wraps a QueryContext failure for that table. Note: for optional tables whose absence is expected (optionalBlockedTable), a table-not-exist error is skipped silently — this error only surfaces for required tables or unexpected errors.

Source

Thrown at internal/storage/issueops/journal_dependencies.go:93

	byKey := make(map[string]journalDependencyEdge)
	for start := 0; start < len(ids); start += deleteBatchSize {
		end := start + deleteBatchSize
		if end > len(ids) {
			end = len(ids)
		}
		inClause, args := buildSQLInClause(ids[start:end])
		queryArgs := append(append([]any{}, args...), args...)
		//nolint:gosec // table is validated above and inClause contains only placeholders.
		rows, err := tx.QueryContext(ctx, fmt.Sprintf(`
			SELECT issue_id, %s AS target, type, metadata
			FROM %s
			WHERE issue_id IN (%s) OR %s IN (%s)
		`, DepTargetExpr, table, inClause, DepTargetExpr, inClause), queryArgs...)
		if err != nil {
			if optionalBlockedTable(table) && isTableNotExistError(err) {
				continue
			}
			return nil, fmt.Errorf("journal: query dependency removals from %s: %w", table, err)
		}
		for rows.Next() {
			var edge journalDependencyEdge
			if err := rows.Scan(&edge.source, &edge.target, &edge.kind, &edge.metadata); err != nil {
				_ = rows.Close()
				return nil, fmt.Errorf("journal: scan dependency removal from %s: %w", table, err)
			}
			byKey[dependencyEdgeKey(edge)] = edge
		}
		if err := rows.Err(); err != nil {
			_ = rows.Close()
			return nil, fmt.Errorf("journal: iterate dependency removals from %s: %w", table, err)
		}
		if err := rows.Close(); err != nil {
			return nil, fmt.Errorf("journal: close dependency removals from %s: %w", table, err)
		}
	}
	return sortedDependencyEdges(byKey), nil

View on GitHub (pinned to 71377f2769)

Solutions

  1. Run migrations to create the missing dependency table (a missing required table is not auto-skipped).
  2. Chunk the issue-ID list so the IN (...) clause and placeholder count stay within driver limits.
  3. Retry on transient lock/connection errors.
  4. Confirm which `table` name appears in the message and check its existence/permissions directly.

Example fix

// before
store.RecordDependencyRemovals(ctx, allIDs) // 50k IDs in one IN clause
// after
for chunk := range slices.Chunk(allIDs, 1000) {
  if err := store.RecordDependencyRemovals(ctx, chunk); err != nil { return err }
}
Defensive patterns

Strategy: validation

Validate before calling

for _, t := range depTables {
    var exists int
    if err := db.QueryRow("SELECT COUNT(*) FROM information_schema.tables WHERE table_name = ?", t).Scan(&exists); err != nil {
        return err
    }
    if exists == 0 && !isOptionalTable(t) { return fmt.Errorf("required table %s missing; run migrations", t) }
}

Try / catch

if err != nil && strings.Contains(err.Error(), "journal: query dependency removals from") {
    if strings.Contains(err.Error(), "doesn't exist") { return runMigrationsAndRetry() }
    if isTransient(err) { return retryWithSmallerBatch(ids) }
    return err
}

Prevention

When it happens

Trigger: RecordDependencyRemovalsForTableInTx / dependencyEdgesForIssueIDsInTx failing on a table: the dependency table is missing and is NOT in the optional set (genuine migration gap), IN-clause too large or malformed, connection loss, or permission denial.

Common situations: Partial migration leaving a required dependency table absent; extremely large issue-ID batches blowing query size limits; locking during schema changes.

Related errors


AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30). Data as JSON: /api/errors/dbb3fb3a8af96c32. Report an issue: GitHub.