gastownhall/beads · warning
cannot modify hooked issue %s (use --force to override)
Error message
cannot modify hooked issue %s (use --force to override)
What it means
This error comes from the NotHooked validator, which protects issues in the special 'hooked' status from accidental modification. Hooked issues are typically locked by an external process or dependency, so the library requires explicit opt-in via a force flag. The message tells the user exactly which issue is protected and how to override.
Source
Thrown at internal/validation/issue.go:244
return func(id string, issue *types.Issue) error {
if issue == nil {
return nil
}
if issue.Status == types.StatusClosed {
return fmt.Errorf("issue %s is already closed", id)
}
return nil
}
}
// NotHooked validates that an issue is not in hooked status.
func NotHooked(force bool) IssueValidator {
return func(id string, issue *types.Issue) error {
if issue == nil {
return nil
}
if !force && issue.Status == types.StatusHooked {
return fmt.Errorf("cannot modify hooked issue %s (use --force to override)", id)
}
return nil
}
}
// HasStatus validates that an issue has one of the allowed statuses.
func HasStatus(allowed ...types.Status) IssueValidator {
return func(id string, issue *types.Issue) error {
if issue == nil {
return nil
}
for _, status := range allowed {
if issue.Status == status {
return nil
}
}
return fmt.Errorf("issue %s has status %s, expected one of: %v", id, issue.Status, allowed)
}View on GitHub (pinned to 71377f2769)
Solutions
- Wait for the owning process to finish and release the hook, then retry the operation
- Confirm no other process is actively working on the issue, then re-run with the force flag (e.g. bd update <id> --force ...)
- Coordinate via the hook owner: close or unhook the issue properly instead of force-overriding
- Add a pre-check of issue.Status != StatusHooked in automation before attempting edits
Example fix
// before
bd.Update(id, changes) // blocked because issue is hooked
// after
issue := getIssue(id)
if issue.Status == types.StatusHooked {
// verify no active owner, then
bd.UpdateForce(id, changes) // or wait and retry without force
} Defensive patterns
Strategy: validation
Validate before calling
func canModify(issue *types.Issue, force bool) bool {
return issue != nil && (force || issue.Status != types.StatusHooked)
} Type guard
func isHooked(issue *types.Issue) bool {
return issue != nil && issue.Status == types.StatusHooked
} Try / catch
if err := updateIssue(id, changes); err != nil {
if strings.Contains(err.Error(), "cannot modify hooked issue") {
return errHookedNeedsForce // surface to user with --force hint
}
return err
} Prevention
- Check for hooked status before editing issues in automation
- Implement hook-aware retry with backoff in multi-agent workflows
- Only use --force after verifying no active owner of the hook
- Log and alert on force-overrides for auditability
When it happens
Trigger: Calling an update/mutation that runs NotHooked on an issue with issue.Status == types.StatusHooked while the force parameter is false. Passing force=true bypasses the check; a nil issue passes.
Common situations: An agent or sync process has marked an issue as hooked (in-progress claim) and another process tries to edit it; a developer manually edits an issue currently claimed by a running workflow; automated scripts colliding with a long-running operation on the same issue.
Related errors
- lock busy: held by another process
- lock already held by another process
- lock already held by another process
- ErrBusy
- timeout waiting for cache lock on %s
AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30).
Data as JSON: /api/errors/05cd4767e5c1f9b4.
Report an issue: GitHub.