gastownhall/beads · error

db: DependencySQLRepository.ListByIssueIDs (out): %w

Error message

db: DependencySQLRepository.ListByIssueIDs (out): %w

What it means

Wraps a failure from the outgoing-direction dependency listing query in ListByIssueIDs (SELECT ... WHERE issue_id IN (...)), executed via queryDeps into result.Outgoing. It means the bulk outgoing-deps fetch for the given issue IDs failed at the SQL/scan level.

Source

Thrown at internal/storage/domain/db/dependency.go:430

		Incoming: make(map[string][]*types.Dependency),
	}
	if len(issueIDs) == 0 {
		return result, nil
	}

	idPlaceholders, idArgs := buildInPlaceholders(issueIDs)
	typeWhere, typeArgs := buildTypeFilter(opts.Types)
	table := pickDepTable(opts.UseWispsTable)

	if opts.Direction == domain.DepDirectionBoth || opts.Direction == domain.DepDirectionOut {
		//nolint:gosec // G201: table and depSelectColumns are hardcoded
		q := fmt.Sprintf(
			`SELECT %s FROM %s WHERE issue_id IN (%s)%s ORDER BY issue_id`,
			depSelectColumns, table, idPlaceholders, typeWhere,
		)
		args := combineArgs(idArgs, typeArgs)
		if err := r.queryDeps(ctx, q, args, result.Outgoing, true); err != nil {
			return domain.DepBulkResult{}, fmt.Errorf("db: DependencySQLRepository.ListByIssueIDs (out): %w", err)
		}
	}

	if opts.Direction == domain.DepDirectionBoth || opts.Direction == domain.DepDirectionIn {
		//nolint:gosec // G201: table, depSelectColumns, depTargetExpr are hardcoded
		q := fmt.Sprintf(
			`SELECT %s FROM %s WHERE %s IN (%s)%s ORDER BY issue_id`,
			depSelectColumns, table, depTargetExpr, idPlaceholders, typeWhere,
		)
		args := combineArgs(idArgs, typeArgs)
		if err := r.queryDeps(ctx, q, args, result.Incoming, false); err != nil {
			return domain.DepBulkResult{}, fmt.Errorf("db: DependencySQLRepository.ListByIssueIDs (in): %w", err)
		}
	}

	return result, nil
}

View on GitHub (pinned to 71377f2769)

Solutions

  1. Chunk issueIDs into batches (e.g. 500-1000) to stay within driver placeholder limits.
  2. Verify opts.UseWispsTable selects the table that actually holds the dependencies.
  3. Re-run on a healthy connection; it's read-only and safe to retry.
  4. Check scan/column mismatch after schema migrations; rebuild or migrate the deps table if columns changed.

Example fix

// before
res, err := deps.ListByIssueIDs(ctx, allThousandIDs, domain.DepListOpts{Direction: domain.DepDirectionBoth})
// after: chunk
for batch := range slices.Chunk(allThousandIDs, 500) {
    r, err := deps.ListByIssueIDs(ctx, batch, domain.DepListOpts{Direction: domain.DepDirectionBoth})
    mergeInto(res, r)
}
Defensive patterns

Strategy: validation

Validate before calling

if len(issueIDs) == 0 { return domain.DepBulkResult{}, nil }
const maxBatch = 500
if len(issueIDs) > maxBatch { /* chunk before calling */ }

Try / catch

if err != nil && strings.Contains(err.Error(), "ListByIssueIDs (out)") {
    // retry read-only, or chunk the ID list and merge results
}

Prevention

When it happens

Trigger: ListByIssueIDs(ctx, issueIDs, opts) with Direction=Both or Out; the SELECT fails or row scanning fails: too many IDs for the driver's placeholder limit, connection error, scan type mismatch, or wrong table via UseWispsTable.

Common situations: Passing thousands of issue IDs blowing past driver placeholder limits; querying the wisps deps table when data lives in the issues deps table; DB restarted mid-batch; schema drift after upgrade.

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