mastra-ai/mastra · error · FactoryDispatchError

session_unavailable

session_unavailable

Error message

No active Factory binding for role ${role}. | No active Factory binding.

What it means

#requireBinding throws a FactoryDispatchError with code 'session_unavailable' when no active run binding exists for the work item (and optionally the requested role). The message names the role when one was requested. It means the factory cannot route the decision to any active session because no binding was ever established or all bindings are inactive.

Source

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

  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];
  }

  async #requireBinding(record: FactoryDeferredDecisionRecord, role?: string): Promise<FactoryRunBindingRecord> {
    const binding = await this.#findBinding(record, role);
    if (!binding) {
      throw new FactoryDispatchError(
        'session_unavailable',
        role ? `No active Factory binding for role ${role}.` : 'No active Factory binding.',
      );
    }
    return binding;
  }

  async #requireOrPrepareBinding(
    record: FactoryDeferredDecisionRecord,
    role: string,
  ): Promise<FactoryRunBindingRecord> {
    const binding = await this.#findBinding(record, role);
    if (binding) {
      const session = await this.#controller.getSessionByResource(binding.resourceId);
      if (session) return binding;
    }
    if (!this.#prepareBinding) {
      throw new FactoryDispatchError(

View on GitHub (pinned to 75dd419e61)

Solutions

  1. Check whether a prepareBinding hook is configured on the dispatcher — if so, prefer the prepare-capable path which creates the binding on demand.
  2. List the item's run bindings and re-activate or create one for the requested role.
  3. Verify you're requesting a role that actually exists for this item (role names are exact matches).
  4. Confirm orgId/factoryProjectId scoping — bindings in another project won't be found.

Example fix

// before
await dispatcher.dispatch(record); // throws for missing 'review' binding
// after
const b = await listBindings(record);
if (!b.some(x => x.role === 'review' && x.status === 'active')) {
  await prepareBinding({ record, item, role: 'review' });
}
await dispatcher.dispatch(record);
Defensive patterns

Strategy: fallback

Validate before calling

const bindings = await storage.listRunBindings(orgId, projectId, workItemId);
if (!bindings.some(b => b.status === 'active' && (role === undefined || b.role === role))) await prepareBinding({ role });

Type guard

function hasActiveBinding(bs: { status: string; role: string }[], role?: string): boolean {
  return bs.some(b => b.status === 'active' && (role === undefined || b.role === role));
}

Try / catch

try {
  await dispatcher.dispatch(record);
} catch (e) {
  if (e instanceof FactoryDispatchError && e.code === 'session_unavailable') {
    await prepareBinding({ record, role });
    await dispatcher.dispatch(record);
  } else throw e;
}

Prevention

When it happens

Trigger: Dispatching a decision whose work item has zero run bindings with status 'active' in listRunBindings(orgId, factoryProjectId, workItemId), either with a specific role (e.g. 'review') or any role.

Common situations: Binding never prepared because the kickoff step failed; a binding was deactivated after run completion/cancellation; requesting a role (e.g. 'review') that was never configured for the item.

Related errors


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