{"record":{"id":"ce85d22b869db104","repo":"mastra-ai/mastra","slug":"session-unavailable","errorCode":"session_unavailable","errorMessage":"No active Factory binding for role ${role}. | No active Factory binding.","messagePattern":"No active Factory binding for role (.+?)\\. \\| No active Factory binding\\.","errorType":"error_code","errorClass":"FactoryDispatchError","httpStatus":null,"severity":"error","filePath":"mastracode/factory/src/rules/dispatcher.ts","lineNumber":832,"sourceCode":"  async #findBinding(\n    record: FactoryDeferredDecisionRecord,\n    role?: string,\n  ): Promise<FactoryRunBindingRecord | undefined> {\n    if (!record.workItemId) throw new Error('Factory decision is not linked to a work item.');\n    const bindings = await this.#storage.listRunBindings(record.orgId, record.factoryProjectId, record.workItemId);\n    return bindings\n      .filter(candidate => candidate.status === 'active' && (role === undefined || candidate.role === role))\n      .sort((left, right) => {\n        if (role === undefined && left.role === 'work' && right.role !== 'work') return -1;\n        if (role === undefined && right.role === 'work' && left.role !== 'work') return 1;\n        return right.createdAt.getTime() - left.createdAt.getTime() || left.id.localeCompare(right.id);\n      })[0];\n  }\n\n  async #requireBinding(record: FactoryDeferredDecisionRecord, role?: string): Promise<FactoryRunBindingRecord> {\n    const binding = await this.#findBinding(record, role);\n    if (!binding) {\n      throw new FactoryDispatchError(\n        'session_unavailable',\n        role ? `No active Factory binding for role ${role}.` : 'No active Factory binding.',\n      );\n    }\n    return binding;\n  }\n\n  async #requireOrPrepareBinding(\n    record: FactoryDeferredDecisionRecord,\n    role: string,\n  ): Promise<FactoryRunBindingRecord> {\n    const binding = await this.#findBinding(record, role);\n    if (binding) {\n      const session = await this.#controller.getSessionByResource(binding.resourceId);\n      if (session) return binding;\n    }\n    if (!this.#prepareBinding) {\n      throw new FactoryDispatchError(","sourceCodeStart":814,"sourceCodeEnd":850,"githubUrl":"https://github.com/mastra-ai/mastra/blob/75dd419e613fe9c39f846ffc500716141b74fda6/mastracode/factory/src/rules/dispatcher.ts#L814-L850","documentation":"#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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\nawait dispatcher.dispatch(record); // throws for missing 'review' binding\n// after\nconst b = await listBindings(record);\nif (!b.some(x => x.role === 'review' && x.status === 'active')) {\n  await prepareBinding({ record, item, role: 'review' });\n}\nawait dispatcher.dispatch(record);","handlingStrategy":"fallback","validationCode":"const bindings = await storage.listRunBindings(orgId, projectId, workItemId);\nif (!bindings.some(b => b.status === 'active' && (role === undefined || b.role === role))) await prepareBinding({ role });","typeGuard":"function hasActiveBinding(bs: { status: string; role: string }[], role?: string): boolean {\n  return bs.some(b => b.status === 'active' && (role === undefined || b.role === role));\n}","tryCatchPattern":"try {\n  await dispatcher.dispatch(record);\n} catch (e) {\n  if (e instanceof FactoryDispatchError && e.code === 'session_unavailable') {\n    await prepareBinding({ record, role });\n    await dispatcher.dispatch(record);\n  } else throw e;\n}","preventionTips":["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."],"tags":["factory","binding","session","dispatch"],"backgroundTag":"session-unavailable","analyzedSha":"75dd419e613fe9c39f846ffc500716141b74fda6","analyzedAt":"2026-08-30T00:15:31.844Z","schemaVersion":2},"datasetVersion":"2026-08-30T03:17:51.788Z"}