mastra-ai/mastra · error

Factory decision is not linked to a work item.

Error message

Factory decision is not linked to a work item.

What it means

#requireItem enforces that a deferred decision record carries a workItemId before doing anything item-related. If record.workItemId is falsy, a plain Error 'Factory decision is not linked to a work item.' is thrown. It indicates a data-integrity problem: a decision was deferred/persisted without ever being linked to a created work item.

Source

Thrown at mastracode/factory/src/rules/dispatcher.ts:808

    if (decision.stage === 'intake') return;

    const moved = await this.#transitionService.transition({
      orgId: record.orgId,
      factoryProjectId: record.factoryProjectId,
      workItemId: result.item.id,
      board,
      stage: decision.stage,
      expectedRevision,
      actor: { type: 'system', id: 'factory-rule-dispatcher' },
      ingress: { type: 'rule', identity: `decision:${record.idempotencyKey}:${result.item.id}:destination` },
      cause: materializedByDecision ? 'linked_item_materialized' : 'linked_item_reconciled',
      causalChain,
    });
    if (moved.status === 'rejected') throw new Error(`${moved.code}: ${moved.reason}`);
  }

  async #requireItem(record: FactoryDeferredDecisionRecord) {
    if (!record.workItemId) throw new Error('Factory decision is not linked to a work item.');
    const item = await this.#storage.get({ orgId: record.orgId, id: record.workItemId });
    if (!item) throw new Error('Factory work item not found.');
    return item;
  }

  async #findBinding(
    record: FactoryDeferredDecisionRecord,
    role?: string,
  ): Promise<FactoryRunBindingRecord | undefined> {
    if (!record.workItemId) throw new Error('Factory decision is not linked to a work item.');
    const bindings = await this.#storage.listRunBindings(record.orgId, record.factoryProjectId, record.workItemId);
    return bindings
      .filter(candidate => candidate.status === 'active' && (role === undefined || candidate.role === role))
      .sort((left, right) => {
        if (role === undefined && left.role === 'work' && right.role !== 'work') return -1;
        if (role === undefined && right.role === 'work' && left.role !== 'work') return 1;
        return right.createdAt.getTime() - left.createdAt.getTime() || left.id.localeCompare(right.id);
      })[0];

View on GitHub (pinned to 75dd419e61)

Solutions

  1. Query storage for decisions with null workItemId and delete or re-enqueue them after re-materializing the item.
  2. Fix the upstream step that should create the work item and set workItemId before deferral completes.
  3. If constructing records manually (tests), always set workItemId to a valid stored item id.

Example fix

// before
dispatch(record); // record.workItemId missing
// after
if (!record.workItemId) record = await reMaterializeAndLink(record);
await dispatcher.dispatch(record);
Defensive patterns

Strategy: validation

Validate before calling

function canDispatch(record: FactoryDeferredDecisionRecord): boolean {
  return typeof record.workItemId === 'string' && record.workItemId.length > 0;
}

Type guard

function isLinkedDecision(r: FactoryDeferredDecisionRecord): r is FactoryDeferredDecisionRecord & { workItemId: string } {
  return typeof r.workItemId === 'string' && r.workItemId.length > 0;
}

Try / catch

try {
  await dispatcher.dispatch(record);
} catch (e) {
  if (e.message === 'Factory decision is not linked to a work item.') await reMaterializeAndLink(record);
  else throw e;
}

Prevention

When it happens

Trigger: Calling dispatcher paths that resolve the work item (#requireItem via binding/session preparation) with a FactoryDeferredDecisionRecord whose workItemId is null/undefined — e.g. a record saved before item creation failed, or a manually constructed record.

Common situations: A previous dispatch crashed between deferral and item creation, leaving an orphan decision record; importing/restoring storage data that lost the link; tests constructing partial records.

Understand the failure class

Background: "missing required argument" and "the following required arguments were not provided": what required-argument errors mean and how to fix them — this error's family across 20 libraries.

Related errors


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