{"record":{"id":"8d320e7e044581b2","repo":"ruvnet/ruflo","slug":"issue-input-issueid-is-not-claimed-by-input-c","errorCode":null,"errorMessage":"Issue ${input.issueId} is not claimed by ${input.claimantId}","messagePattern":"Issue (.+?) is not claimed by (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"v3/@claude-flow/claims/src/api/mcp-tools.ts","lineNumber":568,"sourceCode":"}> {\n  if (context?.claimsService) {\n    const result = await context.claimsService.releaseClaim(input);\n    return {\n      released: result.released,\n      issueId: input.issueId,\n      releasedAt: result.releasedAt,\n      reason: input.reason,\n    };\n  }\n\n  // Simple implementation\n  const issue = issueStore.get(input.issueId);\n  if (!issue) {\n    throw new Error(`Issue not found: ${input.issueId}`);\n  }\n\n  if (issue.claimedBy !== input.claimantId) {\n    throw new Error(`Issue ${input.issueId} is not claimed by ${input.claimantId}`);\n  }\n\n  // Find and update the claim\n  for (const claim of claimStore.values()) {\n    if (claim.issueId === input.issueId && claim.claimantId === input.claimantId) {\n      claim.status = 'released';\n      claim.lastActivityAt = new Date().toISOString();\n    }\n  }\n\n  issue.claimedBy = undefined;\n\n  return {\n    released: true,\n    issueId: input.issueId,\n    releasedAt: new Date().toISOString(),\n    reason: input.reason,\n  };","sourceCodeStart":550,"sourceCodeEnd":586,"githubUrl":"https://github.com/ruvnet/ruflo/blob/fa13ee4ad60ac2090b1480656eb233521790d640/v3/@claude-flow/claims/src/api/mcp-tools.ts#L550-L586","documentation":"Thrown by the release-claim MCP tool handler when the issue exists and is claimed, but issue.claimedBy !== input.claimantId — the caller is not the owner of the claim. This protects against one agent releasing another agent's active claim; note the comparison is on the claimant id string, so type/namespace mismatches (agent:1 vs 1) also trip it.","triggerScenarios":"Calling release with a claimantId different from the one used at claim time; using 'agent:coder-1' as claimantId when the claim was stored under 'coder-1'; supervisors attempting to force-release on behalf of a worker without matching ids.","commonSituations":"Handoff flows where ownership moved to a new agent but the old owner still calls release; id-format drift between subsystems; well-intentioned orchestrators cleaning up worker claims with their own id.","solutions":["Release with the exact claimantId recorded in claim.claimantId (inspect the claim first)","If ownership legitimately moved, complete the handoff flow so the new owner releases it","If the owner is dead, use the steal flow to acquire the claim, then release it as the new owner"],"exampleFix":"// before\nawait releaseTool({ issueId, claimantId: 'agent-orchestrator' }); // claim held by 'agent-worker-1'\n\n// after\nconst claim = await getClaimTool({ issueId });\nawait releaseTool({ issueId, claimantId: claim.claimantId }); // release as the recorded owner","handlingStrategy":"validation","validationCode":"const issue = await getIssueTool({ issueId });\nif (issue.claimedBy !== claimantId) {\n  throw new Error(`cannot release: claim held by ${issue.claimedBy}`);\n}\nawait releaseTool({ issueId, claimantId });","typeGuard":null,"tryCatchPattern":"try { await releaseTool(input); } catch (e) { if (e instanceof Error && e.message.includes('is not claimed by')) { /* fetch real owner and escalate */ } throw e; }","preventionTips":["Always release with the exact claimantId returned from the claim call","Store claimantId alongside the task so shutdown code uses the right identity"],"tags":["claims","mcp-tools","ownership","authorization"],"backgroundTag":"authorization-failed","analyzedSha":"fa13ee4ad60ac2090b1480656eb233521790d640","analyzedAt":"2026-08-18T21:34:22.708Z","contentChangedAt":"2026-08-18T21:34:22.708Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}