gastownhall/beads · error
ErrNotOwner
ErrNotOwner
Error message
issue claimed by a different actor
What it means
ErrNotOwner is returned when an actor tries to release a claim that a different actor holds. Releasing another actor's claim requires the force escape hatch (ReleaseRequest.Force, `bd unclaim --force`) for abandoned claims, or ReleaseRequest.ExpectedAssignee naming the holder. It preserves the identity of storage.ErrNotOwner so existing errors.Is sites keep matching, letting public-role callers classify the refusal without importing internal packages.
Source
Thrown at issueops/errors.go:78
// stale; the issue is left untouched. See Releaser.Release and
// UpdateRequest.ExpectedAssignee.
var ErrAssigneeMismatch = errors.New("assignee mismatch")
// ErrNotOwner is returned when an actor tries to release a claim that a
// different actor holds. Releasing another actor's claim requires the force
// escape hatch (ReleaseRequest.Force, `bd unclaim --force`), reserved for
// abandoned claims — or ReleaseRequest.ExpectedAssignee, which authorizes the
// same thing by naming the holder instead of ignoring it.
//
// It is DECLARED here and re-exported by internal/storage rather than the other
// way round, which is the direction the memoryops slice settled: a Go alias
// preserves identity in both directions, so every existing storage.ErrNotOwner
// reference keeps matching this identical value, and a caller holding only the
// public role can now classify the refusal without importing an internal
// package it cannot reach. It is the ownership half of the same vocabulary
// ErrAssigneeMismatch above belongs to, which is why it is here rather than
// beside Releaser: both refusals answer "whose claim is this".
var ErrNotOwner = errors.New("issue claimed by a different actor")
// The namespace-neutral part of this vocabulary is declared by beadserrors and
// re-exported here. These are ALIASES, so they are the same values: every
// existing issueops.ErrX reference and every errors.Is site keeps matching the
// identical error, and a leaf that never imports issueops still matches it too.
//
// They live down there because none of them names an issue: a request can be
// invalid, a row can be missing and a database can be uninitialized on any
// plane. The refusals BELOW that name issue concepts stay here.
var (
// ErrNotFound is returned when a requested entity does not exist in the database.
ErrNotFound = beadserrors.ErrNotFound
// ErrValidation classifies deterministic request-validation failures.
ErrValidation = beadserrors.ErrValidation
// ErrNotInitialized is returned when the database has not been initialized
// (e.g., issue_prefix config is missing).
ErrNotInitialized = beadserrors.ErrNotInitialized
)View on GitHub (pinned to 71377f2769)
Solutions
- If the claim is abandoned, pass ReleaseRequest.Force = true (or `bd unclaim --force`).
- Alternatively name the holder explicitly with ReleaseRequest.ExpectedAssignee to authorize releasing a foreign claim.
- Verify the actor identity used by your tooling matches the claimant (env/config).
- Have the actual holder release their own claim.
Example fix
// before
err := releaser.Release(ctx, types.ReleaseRequest{Issue: id}) // actor != holder
// after
err := releaser.Release(ctx, types.ReleaseRequest{Issue: id, Force: true})
// or: ExpectedAssignee: "alice" to name the holder Defensive patterns
Strategy: type-guard
Validate before calling
iss, _ := store.GetIssue(ctx, id)
if iss.Assignee != "" && iss.Assignee != actor {
// foreign claim: need Force or ExpectedAssignee
} Try / catch
if errors.Is(err, issueops.ErrNotOwner) {
// retry with ReleaseRequest.Force or ExpectedAssignee naming the holder
} Prevention
- Match the actor identity used at claim time in automation.
- Reserve Force for genuinely abandoned claims.
- Prefer ExpectedAssignee to name the holder explicitly.
When it happens
Trigger: Calling Releaser.Release (or UnclaimIssueInTx) as actor X on an issue claimed by actor Y without Force or ExpectedAssignee; scripted cleanup running under a service account trying to unclaim user-held issues.
Common situations: Cleaning up a departed teammate's abandoned claims; CI bots attempting to unclaim human-held issues; mismatched actor identity (different username/env) from the one that claimed.
Related errors
- ErrAlreadyClaimed
- ErrNotClaimable
- ready claim of %s reported success but could not be verified
- db: Claim %s: read old issue: %w
- db: Claim %s: resolve claim pools: %w
AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30).
Data as JSON: /api/errors/a721430092139d7e.
Report an issue: GitHub.