gastownhall/beads · error
Unclaim: id must not be empty
Error message
Unclaim: id must not be empty
What it means
Validation error from Unclaim: the use case rejects the call because the issue id argument is an empty string. This is thrown before any repository interaction, so nothing was modified. It is a caller contract violation, not a storage failure.
Source
Thrown at internal/storage/domain/issue.go:1880
func (u *issueUseCaseImpl) GetStaleIssues(ctx context.Context, filter types.StaleFilter) ([]*types.Issue, error) {
out, err := u.issueRepo.GetStaleIssues(ctx, filter)
if err != nil {
return nil, fmt.Errorf("GetStaleIssues: %w", err)
}
return out, nil
}
func (u *issueUseCaseImpl) GetEpicsEligibleForClosure(ctx context.Context) ([]*types.EpicStatus, error) {
out, err := u.issueRepo.GetEpicsEligibleForClosure(ctx)
if err != nil {
return nil, fmt.Errorf("GetEpicsEligibleForClosure: %w", err)
}
return out, nil
}
func (u *issueUseCaseImpl) Unclaim(ctx context.Context, id, actor string, force bool) error {
if id == "" {
return fmt.Errorf("Unclaim: id must not be empty")
}
if err := u.issueRepo.UnclaimIssue(ctx, id, actor, force); err != nil {
return fmt.Errorf("Unclaim: %w", err)
}
return nil
}
// UnclaimIfAssignee is the compare-and-swap release: it clears the claim only
// while the issue is still assigned to expectedAssignee, and otherwise returns
// storage.ErrAssigneeMismatch having written nothing. It is the conditional
// twin of Unclaim and runs the SAME transition (assignee cleared, status
// reopened, started_at cleared, lease dropped, row_lock rewritten, "unclaimed"
// event recorded) because both reach the one classic implementation in
// issueops — which is what makes `bd unclaim --if-assignee` behave identically
// on the proxied-server and embedded backends.
func (u *issueUseCaseImpl) UnclaimIfAssignee(ctx context.Context, id, actor, expectedAssignee string) error {
if id == "" {
return fmt.Errorf("UnclaimIfAssignee: id must not be empty")View on GitHub (pinned to 71377f2769)
Solutions
- Ensure the issue id is resolved before calling Unclaim (check the source of the empty value)
- Validate the id is non-empty in your caller before invoking
- If the id comes from user input, surface a clear 'issue key required' message
Example fix
// before
if err := uc.Unclaim(ctx, issueID, actor, false); err != nil { ... }
// after
if issueID == "" {
return fmt.Errorf("issue id required")
}
if err := uc.Unclaim(ctx, issueID, actor, false); err != nil { ... } Defensive patterns
Strategy: validation
Validate before calling
if id == "" {
return fmt.Errorf("cannot unclaim: issue id is empty")
} Try / catch
if err := uc.Unclaim(ctx, id, actor, force); err != nil {
if strings.Contains(err.Error(), "id must not be empty") {
return fmt.Errorf("caller bug: empty issue id")
}
return err
} Prevention
- Validate issue ids at the boundary (CLI args, JSON fields) before they reach storage calls
- Fail fast on empty lookups instead of threading empty strings downward
- Use a small helper to assert non-empty keys before any uc.* call
When it happens
Trigger: Calling Unclaim(ctx, "", actor, force) — passing an empty id, typically when a variable holding the issue key was never populated.
Common situations: Parsing a CLI arg or JSON field that is missing/empty and passing it straight to Unclaim; empty results from upstream lookups feeding the claim-release path.
Related errors
- loop %q: count must be positive
- UnclaimIfAssignee: id must not be empty
- %w: claim cannot use expected assignee or status
- ErrNotClaimable
- %w: claim next requires an actor
AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30).
Data as JSON: /api/errors/8f91793d77ee7f47.
Report an issue: GitHub.