gastownhall/beads · error

iter dep metadata: sourceID must not be empty

Error message

iter dep metadata: sourceID must not be empty

What it means

Validation guard in iterWithMetadata: the source ID for iterator-based dependency metadata listing is empty. Mirrors the listWithMetadata check but for the streaming/iterator API.

Source

Thrown at internal/storage/domain/dependency.go:534

func (u *dependencyUseCaseImpl) listWithMetadata(ctx context.Context, sourceID string, filter DepListFilter, useWisp bool) ([]*types.IssueWithDependencyMetadata, error) {
	if sourceID == "" {
		return nil, fmt.Errorf("list dep metadata: sourceID must not be empty")
	}
	out, err := u.depRepo.ListWithIssueMetadata(ctx, sourceID, DepListOpts{
		Types:         filter.Types,
		Direction:     filter.Direction,
		UseWispsTable: useWisp,
	})
	if err != nil {
		return nil, fmt.Errorf("list dep metadata: %w", err)
	}
	return out, nil
}

func (u *dependencyUseCaseImpl) iterWithMetadata(ctx context.Context, sourceID string, filter DepListFilter, useWisp bool) (storage.Iter[types.IssueWithDependencyMetadata], error) {
	if sourceID == "" {
		return nil, fmt.Errorf("iter dep metadata: sourceID must not be empty")
	}
	it, err := u.depRepo.IterWithIssueMetadata(ctx, sourceID, DepListOpts{
		Types:         filter.Types,
		Direction:     filter.Direction,
		UseWispsTable: useWisp,
	})
	if err != nil {
		return nil, fmt.Errorf("iter dep metadata: %w", err)
	}
	return it, nil
}

func (u *dependencyUseCaseImpl) countByID(ctx context.Context, sourceID string, filter DepListFilter, useWisp bool) (int64, error) {
	if sourceID == "" {
		return 0, fmt.Errorf("count by id: sourceID must not be empty")
	}
	n, err := u.depRepo.CountByID(ctx, sourceID, DepListOpts{
		Types:         filter.Types,

View on GitHub (pinned to 71377f2769)

Solutions

  1. Persist/fetch the issue and use its real ID before iterating
  2. Guard with a non-empty check at the call site
  3. Audit paths where issue IDs come from user input or optional lookups

Example fix

// before
it, err := u.IterWithIssueMetadata(ctx, srcID, filter) // srcID == ""
// after
if srcID == "" {
	return fmt.Errorf("source id required")
}
it, err := u.IterWithIssueMetadata(ctx, srcID, filter)
Defensive patterns

Strategy: validation

Validate before calling

if sourceID == "" {
	return fmt.Errorf("cannot iterate dep metadata: source ID is empty")
}

Type guard

func hasID(id string) bool { return id != "" }

Try / catch

it, err := u.IterWithIssueMetadata(ctx, sourceID, filter)
if err != nil && strings.Contains(err.Error(), "iter dep metadata: sourceID") {
	return fmt.Errorf("caller bug: empty source id")
}

Prevention

When it happens

Trigger: Calling IterWithIssueMetadata or IterWispWithIssueMetadata with sourceID == "".

Common situations: Same as listWithMetadata: unsaved issue objects, failed lookups passed through, or zero-value string IDs in Go callers.

Related errors


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