{"record":{"id":"a721430092139d7e","repo":"gastownhall/beads","slug":"errnotowner","errorCode":"ErrNotOwner","errorMessage":"issue claimed by a different actor","messagePattern":"issue claimed by a different actor","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"error","filePath":"issueops/errors.go","lineNumber":78,"sourceCode":"// 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\n// re-exported here. These are ALIASES, so they are the same values: every\n// existing issueops.ErrX reference and every errors.Is site keeps matching the\n// identical error, and a leaf that never imports issueops still matches it too.\n//\n// They live down there because none of them names an issue: a request can be\n// invalid, a row can be missing and a database can be uninitialized on any\n// plane. The refusals BELOW that name issue concepts stay here.\nvar (\n\t// ErrNotFound is returned when a requested entity does not exist in the database.\n\tErrNotFound = beadserrors.ErrNotFound\n\t// ErrValidation classifies deterministic request-validation failures.\n\tErrValidation = beadserrors.ErrValidation\n\t// ErrNotInitialized is returned when the database has not been initialized\n\t// (e.g., issue_prefix config is missing).\n\tErrNotInitialized = beadserrors.ErrNotInitialized\n)","sourceCodeStart":60,"sourceCodeEnd":96,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/issueops/errors.go#L60-L96","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\nerr := releaser.Release(ctx, types.ReleaseRequest{Issue: id}) // actor != holder\n\n// after\nerr := releaser.Release(ctx, types.ReleaseRequest{Issue: id, Force: true})\n// or: ExpectedAssignee: \"alice\" to name the holder","handlingStrategy":"type-guard","validationCode":"iss, _ := store.GetIssue(ctx, id)\nif iss.Assignee != \"\" && iss.Assignee != actor {\n    // foreign claim: need Force or ExpectedAssignee\n}","typeGuard":null,"tryCatchPattern":"if errors.Is(err, issueops.ErrNotOwner) {\n    // retry with ReleaseRequest.Force or ExpectedAssignee naming the holder\n}","preventionTips":["Match the actor identity used at claim time in automation.","Reserve Force for genuinely abandoned claims.","Prefer ExpectedAssignee to name the holder explicitly."],"tags":["claim","ownership","authorization"],"backgroundTag":"not-claim-owner","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}