gastownhall/beads · error

count by id: sourceID must not be empty

Error message

count by id: sourceID must not be empty

What it means

Validation guard in countByID: the source ID for counting dependencies is empty. CountByIssueID/CountByWispID cannot meaningfully count for a blank ID, so the use-case rejects it up front.

Source

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

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,
		Direction:     filter.Direction,
		UseWispsTable: useWisp,
	})
	if err != nil {
		return 0, fmt.Errorf("count by id: %w", err)
	}
	return n, nil
}

func (u *dependencyUseCaseImpl) list(ctx context.Context, ids []string, filter DepListFilter, useWisp bool) (DepBulkResult, error) {
	if len(ids) == 0 {
		return DepBulkResult{
			Outgoing: map[string][]*types.Dependency{},
			Incoming: map[string][]*types.Dependency{},
		}, nil

View on GitHub (pinned to 71377f2769)

Solutions

  1. Fetch a persisted issue ID before counting
  2. Add an empty-string check before the count call
  3. Confirm the wisp/issue was created (Create returns the ID you should reuse)

Example fix

// before
n, err := u.CountByIssueID(ctx, issueID, filter) // issueID == ""
// after
if issueID == "" {
	return 0, fmt.Errorf("issueID required")
}
n, err := u.CountByIssueID(ctx, issueID, filter)
Defensive patterns

Strategy: validation

Validate before calling

if wispID == "" {
	return fmt.Errorf("cannot count deps: ID is empty")
}

Type guard

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

Try / catch

n, err := u.CountByWispID(ctx, wispID, filter)
if err != nil && strings.Contains(err.Error(), "sourceID must not be empty") {
	// caller bug: not retryable; fix ID sourcing
}

Prevention

When it happens

Trigger: Calling CountByIssueID or CountByWispID with sourceID == "".

Common situations: Caller passed an ID from an unsaved or deleted issue; a variable initialized to "" used as a placeholder; upstream filtering produced an empty ID string.

Related errors


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