gastownhall/beads · error

GetNewlyUnblockedByClose: closedID must not be empty

Error message

GetNewlyUnblockedByClose: closedID must not be empty

What it means

A guard-clause validation error from getNewlyUnblockedByClose: it refuses to run when closedID is an empty string. The library requires the ID of the issue that was just closed to compute which dependents became unblocked; an empty ID is a programming/caller bug, not a storage failure. Unlike its sibling error at line 1782, nothing is wrapped — this is raised before any repository call.

Source

Thrown at internal/storage/domain/issue.go:1778

	for _, child := range children {
		if child.Status != types.StatusClosed {
			open++
		}
	}
	return open, nil
}

func (u *issueUseCaseImpl) GetNewlyUnblockedByClose(ctx context.Context, closedID string) ([]*types.Issue, error) {
	return u.getNewlyUnblockedByClose(ctx, closedID)
}

func (u *issueUseCaseImpl) GetNewlyUnblockedByCloseWisp(ctx context.Context, closedID string) ([]*types.Issue, error) {
	return u.getNewlyUnblockedByClose(ctx, closedID)
}

func (u *issueUseCaseImpl) getNewlyUnblockedByClose(ctx context.Context, closedID string) ([]*types.Issue, error) {
	if closedID == "" {
		return nil, fmt.Errorf("GetNewlyUnblockedByClose: closedID must not be empty")
	}
	out, err := u.issueRepo.GetNewlyUnblockedByClose(ctx, closedID)
	if err != nil {
		return nil, fmt.Errorf("GetNewlyUnblockedByClose %s: %w", closedID, err)
	}
	return out, nil
}

func (u *issueUseCaseImpl) ClaimReadyIssue(ctx context.Context, filter types.WorkFilter, actor string) (ClaimReadyResult, error) {
	return u.claimReady(ctx, filter, actor, false)
}

func (u *issueUseCaseImpl) ClaimReadyWisp(ctx context.Context, filter types.WorkFilter, actor string) (ClaimReadyResult, error) {
	return u.claimReady(ctx, filter, actor, true)
}

func (u *issueUseCaseImpl) claimReady(ctx context.Context, filter types.WorkFilter, actor string, useWisp bool) (ClaimReadyResult, error) {
	var (

View on GitHub (pinned to 71377f2769)

Solutions

  1. Ensure the closed issue's ID is captured before calling; log or check it at the call site
  2. Skip the call entirely when the ID is empty (empty ID means nothing to look up)
  3. Trace upstream to find why the ID was zero-valued (missing JSON field, unfilled struct, dropped CLI flag)

Example fix

// before
unblocked, err := uc.GetNewlyUnblockedByClose(ctx, closedID)
// after
if closedID == "" {
    return nil // nothing closed; skip
}
unblocked, err := uc.GetNewlyUnblockedByClose(ctx, closedID)
Defensive patterns

Strategy: validation

Validate before calling

if closedID == "" {
    return nil // skip: nothing was closed
}
// safe to call
out, err := uc.GetNewlyUnblockedByClose(ctx, closedID)

Type guard

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

Prevention

When it happens

Trigger: Calling GetNewlyUnblockedByClose or GetNewlyUnblockedByCloseWisp with an empty closedID string, typically because an upstream variable holding the closed issue's ID was never populated (empty result, missing field, or zero-value Issue.ID).

Common situations: Hook/workflow code reacting to an issue close event where the event payload's ID field is empty; constructing the call from a parsed CLI argument that was omitted; deserializing JSON into a struct where the id key name mismatched.

Understand the failure class

Background: "missing required argument" and "the following required arguments were not provided": what required-argument errors mean and how to fix them — this error's family across 20 libraries.

Related errors


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