{"record":{"id":"a2460d2e71f7c7bb","repo":"gastownhall/beads","slug":"errassigneemismatch","errorCode":"ErrAssigneeMismatch","errorMessage":"assignee mismatch","messagePattern":"assignee mismatch","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"issueops/errors.go","lineNumber":62,"sourceCode":"func (e *ClaimConflictError) Unwrap() error { return e.Err }\n\n// ErrUnsupported reports a capability this backend does not serve. It is an\n// alias of beadserrors.ErrUnsupported — the same type, so one errors.As arm\n// matches it under either name — and it is re-exported here because a caller\n// holding an issueops role should not have to discover a second package to\n// classify the refusal.\n//\n// It is declared there rather than here because the capability shell is not an\n// issue concept: a memory role can go unimplemented by a backend exactly as a\n// Reader can.\ntype ErrUnsupported = beadserrors.ErrUnsupported\n\n// ErrAssigneeMismatch is returned by a conditional release or update whose\n// ExpectedAssignee does not match the issue's current assignee (including when\n// the issue is no longer assigned at all). The caller's view of the claim was\n// stale; the issue is left untouched. See Releaser.Release and\n// UpdateRequest.ExpectedAssignee.\nvar ErrAssigneeMismatch = errors.New(\"assignee mismatch\")\n\n// ErrNotOwner is returned when an actor tries to release a claim that a\n// different actor holds. Releasing another actor's claim requires the force\n// escape hatch (ReleaseRequest.Force, `bd unclaim --force`), reserved for\n// abandoned claims — or ReleaseRequest.ExpectedAssignee, which authorizes the\n// same thing by naming the holder instead of ignoring it.\n//\n// It is DECLARED here and re-exported by internal/storage rather than the other\n// way round, which is the direction the memoryops slice settled: a Go alias\n// preserves identity in both directions, so every existing storage.ErrNotOwner\n// reference keeps matching this identical value, and a caller holding only the\n// public role can now classify the refusal without importing an internal\n// package it cannot reach. It is the ownership half of the same vocabulary\n// ErrAssigneeMismatch above belongs to, which is why it is here rather than\n// beside Releaser: both refusals answer \"whose claim is this\".\nvar ErrNotOwner = errors.New(\"issue claimed by a different actor\")\n\n// The namespace-neutral part of this vocabulary is declared by beadserrors and","sourceCodeStart":44,"sourceCodeEnd":80,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/issueops/errors.go#L44-L80","documentation":"ErrAssigneeMismatch is returned by a conditional release or update whose ExpectedAssignee does not match the issue's current assignee, including when the issue is no longer assigned at all. It means the caller's view of the claim was stale; the issue is left untouched. See Releaser.Release and UpdateRequest.ExpectedAssignee.","triggerScenarios":"Calling Releaser.Release or issuing an update with ExpectedAssignee set while the row's assignee changed (someone else claimed it, or it was unclaimed); batch apply evaluating expected assignee as modified; conditional-guard lifecycle updates with a respelled or stale assignee.","commonSituations":"Long-running automation holding a stale assignee snapshot; two agents coordinating via optimistic guards and one losing; an issue unclaimed between read and conditional write.","solutions":["Re-read the issue to get its current assignee and retry the conditional operation with the fresh value.","If your intent was to take over, use the appropriate claim/ownership flow instead of ExpectedAssignee.","Treat it as an optimistic-concurrency loss: errors.Is(err, issueops.ErrAssigneeMismatch) and re-run your decision logic on fresh state.","If no one should hold it, unclaim first, then perform the conditional operation."],"exampleFix":"// before\nerr := releaser.Release(ctx, types.ReleaseRequest{Issue: id, ExpectedAssignee: \"alice\"})\n// fails if alice already unclaimed\n\n// after\niss, _ := store.GetIssue(ctx, id)\nerr := releaser.Release(ctx, types.ReleaseRequest{Issue: id, ExpectedAssignee: iss.Assignee})\nif errors.Is(err, issueops.ErrAssigneeMismatch) { /* re-read and retry */ }","handlingStrategy":"retry","validationCode":"iss, _ := store.GetIssue(ctx, id)\nif iss.Assignee != expected {\n    expected = iss.Assignee // refresh before conditional op\n}","typeGuard":null,"tryCatchPattern":"if errors.Is(err, issueops.ErrAssigneeMismatch) {\n    // re-read fresh assignee and retry the conditional operation\n}","preventionTips":["Re-read the issue immediately before conditional release/update.","Keep ExpectedAssignee sourced from a fresh read, not cached state.","Treat mismatch as optimistic-concurrency loss and re-run decision logic."],"tags":["optimistic-concurrency","assignee","conflict"],"backgroundTag":"assignee-mismatch","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}