gastownhall/beads · error
force releases whoever holds the issue and expected-assignee
Error message
force releases whoever holds the issue and expected-assignee releases only a named holder; a request cannot ask for both
What it means
ValidateReleaseRequest refuses a request that sets both ExpectedAssignee (non-nil, non-empty) and Force=true. Force releases whoever currently holds the issue; ExpectedAssignee releases only a named holder. They answer the same question and disagree, so a single request cannot ask for both — mirroring UpdateRequest's rule for ForceAssigneeTransfer beside its own ExpectedAssignee.
Source
Thrown at internal/workapi/release.go:44
return fmt.Errorf("%w: release requires an actor to attribute it to", issueops.ErrValidation)
}
if strings.TrimSpace(in.IssueID) == "" {
return fmt.Errorf("%w: release requires an issue id", issueops.ErrValidation)
}
if in.ExpectedAssignee != nil {
// A non-nil pointer to "" is NOT "expected unassigned" here, unlike
// UpdateRequest.ExpectedAssignee: releasing a row nobody holds is not a
// release, and the raw seam beneath this role refuses the empty
// expectation in as many words.
if strings.TrimSpace(*in.ExpectedAssignee) == "" {
return fmt.Errorf("%w: expected assignee must name a holder; there is no release of an unheld issue",
issueops.ErrValidation)
}
// The two are answers to the same question and they disagree, which is
// the rule UpdateRequest states for ForceAssigneeTransfer beside its
// own ExpectedAssignee.
if in.Force {
return fmt.Errorf("%w: force releases whoever holds the issue and expected-assignee releases only a named holder; a request cannot ask for both",
issueops.ErrValidation)
}
}
return nil
}
View on GitHub (pinned to 71377f2769)
Solutions
- Set Force=false and keep ExpectedAssignee to release only if the named holder still holds it
- Set ExpectedAssignee=nil and keep Force=true to release whoever holds it
- Add mutual-exclusion validation at the CLI/config layer so the two options cannot be combined
Example fix
// before
req := issueops.ReleaseRequest{Actor: "alice", IssueID: "bd-1", ExpectedAssignee: &holder, Force: true}
// after
req := issueops.ReleaseRequest{Actor: "alice", IssueID: "bd-1", ExpectedAssignee: &holder} // drop Force, or drop ExpectedAssignee Defensive patterns
Strategy: validation
Validate before calling
if req.Force && req.ExpectedAssignee != nil {
return fmt.Errorf("cannot combine Force with ExpectedAssignee")
} Type guard
func releaseModeIsValid(req issueops.ReleaseRequest) bool {
return !(req.Force && req.ExpectedAssignee != nil)
} Try / catch
if err := releaser.Release(ctx, req); err != nil {
if errors.Is(err, issueops.ErrValidation) {
return fmt.Errorf("conflicting release options: %w", err)
}
return err
} Prevention
- Expose Force and ExpectedAssignee as mutually exclusive CLI flags (flag level mutual exclusion)
- Decide the release mode first (conditional vs forced) and build the request from one branch only
- Add a lint/test asserting the two are never set together in request-construction code
When it happens
Trigger: Calling a Releaser with issueops.ReleaseRequest{Actor: set, IssueID: set, ExpectedAssignee: ptr("bob"), Force: true} — both the conditional-guard and the unconditional-force semantics requested at once.
Common situations: A CLI tool exposed both --force and --expected-assignee and passed both through without mutual exclusion; a caller merged two request-building code paths, one setting Force and one setting ExpectedAssignee.
Related errors
- release requires an actor to attribute it to
- release requires an issue id
- expected assignee must name a holder; there is no release of
- no store is open for this workspace
- not found
AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30).
Data as JSON: /api/errors/86a79c36769597a1.
Report an issue: GitHub.