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

  1. Wait for the owning process to finish and release the hook, then retry the operation
  2. Confirm no other process is actively working on the issue, then re-run with the force flag (e.g. bd update <id> --force ...)
  3. Coordinate via the hook owner: close or unhook the issue properly instead of force-overriding
  4. 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

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


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