mastra-ai/mastra · error · WorkItemRelationError

A work item cannot relate to itself.

Error message

A work item cannot relate to itself.

What it means

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.

Source

Thrown at mastracode/factory/src/storage/domains/work-items/base.ts:599

function priorState(row: WorkItemDbRow): WorkItemPriorState {
  return { stages: row.stages, sessionRoles: Object.keys(row.sessions) };
}

export class WorkItemRelationError extends Error {
  readonly code = 'invalid_work_item_relation';
}

export function validateParentRelation(
  projectItems: WorkItemRow[],
  itemId: string | undefined,
  parentWorkItemId: string | null,
): void {
  if (parentWorkItemId === null) return;
  const byId = new Map(projectItems.map(item => [item.id, item]));
  const parent = byId.get(parentWorkItemId);
  if (!parent) throw new WorkItemRelationError('Related work item not found in this project.');
  if (itemId === parentWorkItemId) throw new WorkItemRelationError('A work item cannot relate to itself.');

  const visited = new Set<string>();
  let cursor: WorkItemRow | undefined = parent;
  while (cursor?.parentWorkItemId) {
    if (cursor.parentWorkItemId === itemId) {
      throw new WorkItemRelationError('This relationship would create a cycle.');
    }
    if (visited.has(cursor.id)) throw new WorkItemRelationError('The related work item chain contains a cycle.');
    visited.add(cursor.id);
    cursor = byId.get(cursor.parentWorkItemId);
  }
}

/**
 * Diff `oldStages` → `newStages` and return the updated history: exited stages
 * get `exitedAt` + `exitedBy` stamped on their open entry, entered stages get
 * a new entry.
 */

View on GitHub (pinned to 75dd419e61)

Solutions

  1. Guard before the call: skip or reject when parentWorkItemId === itemId
  2. In UI code, exclude the current item from the parent picker options
  3. Catch WorkItemRelationError and show a validation message instead of retrying

Example fix

// before
await workItems.update({ id, parentWorkItemId: parentId });
// after
if (parentId !== id) {
  await workItems.update({ id, parentWorkItemId: parentId });
}
Defensive patterns

Strategy: validation

Validate before calling

if (parentWorkItemId === itemId) throw new Error('A work item cannot be its own parent');

Type guard

null

Try / catch

try {
  await workItems.update({ id, parentWorkItemId });
} catch (e) {
  if (e instanceof WorkItemRelationError && e.message.includes('relate to itself')) {
    // reject the form submission / fix the payload
  } else throw e;
}

Prevention

When it happens

Trigger: Calling workItems create or update with parentWorkItemId equal to the item's own id (or the id being upserted).

Common situations: 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.

Related errors


AI-assisted analysis of mastra-ai/mastra@75dd419e61 (2026-08-30). Data as JSON: /api/errors/fdb80f2568ad72b3. Report an issue: GitHub.