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
- Check whether a prepareBinding hook is configured on the dispatcher — if so, prefer the prepare-capable path which creates the binding on demand.
- List the item's run bindings and re-activate or create one for the requested role.
- Verify you're requesting a role that actually exists for this item (role names are exact matches).
- 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
- Always configure a prepareBinding hook so bindings can be created on demand.
- Create the binding as part of item materialization, before deferring decisions.
- Match role names exactly; prefer the role-less call when any role is acceptable.
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
- ${initial.code}: ${initial.reason}
- ${moved.code}: ${moved.reason}
- Factory decision is not linked to a work item.
- Factory work item not found.
- Factory binding ${binding.id} has no authenticated session o
AI-assisted analysis of mastra-ai/mastra@75dd419e61 (2026-08-30).
Data as JSON: /api/errors/ce85d22b869db104.
Report an issue: GitHub.