{"record":{"id":"fdb80f2568ad72b3","repo":"mastra-ai/mastra","slug":"a-work-item-cannot-relate-to-itself","errorCode":null,"errorMessage":"A work item cannot relate to itself.","messagePattern":"A work item cannot relate to itself\\.","errorType":"exception","errorClass":"WorkItemRelationError","httpStatus":null,"severity":"error","filePath":"mastracode/factory/src/storage/domains/work-items/base.ts","lineNumber":599,"sourceCode":"\nfunction priorState(row: WorkItemDbRow): WorkItemPriorState {\n  return { stages: row.stages, sessionRoles: Object.keys(row.sessions) };\n}\n\nexport class WorkItemRelationError extends Error {\n  readonly code = 'invalid_work_item_relation';\n}\n\nexport function validateParentRelation(\n  projectItems: WorkItemRow[],\n  itemId: string | undefined,\n  parentWorkItemId: string | null,\n): void {\n  if (parentWorkItemId === null) return;\n  const byId = new Map(projectItems.map(item => [item.id, item]));\n  const parent = byId.get(parentWorkItemId);\n  if (!parent) throw new WorkItemRelationError('Related work item not found in this project.');\n  if (itemId === parentWorkItemId) throw new WorkItemRelationError('A work item cannot relate to itself.');\n\n  const visited = new Set<string>();\n  let cursor: WorkItemRow | undefined = parent;\n  while (cursor?.parentWorkItemId) {\n    if (cursor.parentWorkItemId === itemId) {\n      throw new WorkItemRelationError('This relationship would create a cycle.');\n    }\n    if (visited.has(cursor.id)) throw new WorkItemRelationError('The related work item chain contains a cycle.');\n    visited.add(cursor.id);\n    cursor = byId.get(cursor.parentWorkItemId);\n  }\n}\n\n/**\n * Diff `oldStages` → `newStages` and return the updated history: exited stages\n * get `exitedAt` + `exitedBy` stamped on their open entry, entered stages get\n * a new entry.\n */","sourceCodeStart":581,"sourceCodeEnd":617,"githubUrl":"https://github.com/mastra-ai/mastra/blob/75dd419e613fe9c39f846ffc500716141b74fda6/mastracode/factory/src/storage/domains/work-items/base.ts#L581-L617","documentation":"Work items cannot be their own parent. validateParentRelation compares itemId to parentWorkItemId and throws WorkItemRelationError when a create/update would make an item relate to itself.","triggerScenarios":"Calling workItems create or update with parentWorkItemId equal to the item's own id (or the id being upserted).","commonSituations":"Form/UI round-trips where the item's own id is accidentally submitted as its parent, bulk sync code that copies parentId without excluding self, or patching an item after it was promoted to a parent.","solutions":["Guard before the call: skip or reject when parentWorkItemId === itemId","In UI code, exclude the current item from the parent picker options","Catch WorkItemRelationError and show a validation message instead of retrying"],"exampleFix":"// before\nawait workItems.update({ id, parentWorkItemId: parentId });\n// after\nif (parentId !== id) {\n  await workItems.update({ id, parentWorkItemId: parentId });\n}","handlingStrategy":"validation","validationCode":"if (parentWorkItemId === itemId) throw new Error('A work item cannot be its own parent');","typeGuard":"null","tryCatchPattern":"try {\n  await workItems.update({ id, parentWorkItemId });\n} catch (e) {\n  if (e instanceof WorkItemRelationError && e.message.includes('relate to itself')) {\n    // reject the form submission / fix the payload\n  } else throw e;\n}","preventionTips":["Exclude the current item from parent-selection UI options","Strip self-references in sync/import code before writing parentWorkItemId","Add a unit test asserting self-parenting is rejected"],"tags":["storage","work-items","validation"],"backgroundTag":"self-referential-relation","analyzedSha":"75dd419e613fe9c39f846ffc500716141b74fda6","analyzedAt":"2026-08-30T00:15:31.844Z","schemaVersion":2},"datasetVersion":"2026-08-30T08:17:16.595Z"}