{"record":{"id":"a5ef10593398a49b","repo":"mastra-ai/mastra","slug":"factory-binding-binding-id-has-no-authenticated","errorCode":null,"errorMessage":"Factory binding ${binding.id} has no authenticated session owner.","messagePattern":"Factory binding (.+?) has no authenticated session owner\\.","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"mastracode/factory/src/rules/dispatcher.ts","lineNumber":928,"sourceCode":"        async leaseExpiresAt =>\n          this.#storage.renewPendingStartLease(leaseIdentity(record, this.#ownerId), leaseExpiresAt),\n        async () => {\n          if (record.message === null) return;\n          const bindings = await this.#storage.listRunBindings(record.orgId, record.factoryProjectId);\n          const binding = bindings.find(\n            candidate => candidate.id === record.bindingId && candidate.status === 'active',\n          );\n          if (!binding) {\n            throw new FactoryDispatchError(\n              'session_unavailable',\n              'Prepared Factory binding is unavailable or revoked.',\n            );\n          }\n          // Wake runs build the Factory workspace, which requires the\n          // authenticated session owner on the request context.\n          const item = await this.#storage.get({ orgId: record.orgId, id: binding.workItemId });\n          const startedBy = item?.sessions[binding.role]?.startedBy;\n          if (!startedBy) throw new Error(`Factory binding ${binding.id} has no authenticated session owner.`);\n          await this.#primeCredentials?.({ orgId: record.orgId, userId: startedBy });\n          const requestContext = new RequestContext();\n          requestContext.set('user', { workosId: startedBy, organizationId: record.orgId });\n          const session = await this.#requireSession(binding);\n          let resolveAgentEnd!: () => void;\n          let agentEnd!: Promise<void>;\n          // The run's own verdict, not the delivery's: a kickoff delivered\n          // into a run that is already terminating is consumed without\n          // execution, and completing the pending start on the delivery ack\n          // alone strands the card with a success ledger entry.\n          let endReason: 'complete' | 'aborted' | 'error' | 'suspended' | undefined;\n          const armAgentEnd = () => {\n            endReason = undefined;\n            agentEnd = new Promise<void>(resolve => {\n              resolveAgentEnd = resolve;\n            });\n          };\n          armAgentEnd();","sourceCodeStart":910,"sourceCodeEnd":946,"githubUrl":"https://github.com/mastra-ai/mastra/blob/75dd419e613fe9c39f846ffc500716141b74fda6/mastracode/factory/src/rules/dispatcher.ts#L910-L946","documentation":"After finding an active binding, the dispatcher fetches the work item and reads the session's startedBy user for the binding's role. Wake runs must execute under the authenticated user who started the session (credentials are primed and a RequestContext user is set), so if startedBy is missing the library cannot attribute or authorize the run and throws immediately. This is a plain Error, recorded on the pending start and retried with backoff.","triggerScenarios":"The work item row exists but item.sessions[binding.role] is undefined, or that session exists without a startedBy field — typically sessions created outside the authenticated flow, partially-migrated rows, or a role mismatch between the binding and stored sessions.","commonSituations":"Sessions seeded by scripts/imports that omit startedBy; a binding whose role was renamed or re-pointed after the session map was written; older schema rows predating the startedBy field; tests that stub work items without session metadata.","solutions":["Ensure the session for binding.role has startedBy populated — re-authenticate/record the starting user on the work item session.","Check that binding.role matches an actual key in item.sessions (e.g. 'implement' vs 'review') and fix the binding's role if it was renamed.","Backfill startedBy for imported/migrated work item rows before re-enabling dispatch.","If the item is missing entirely (get returned undefined), re-create the work item for binding.workItemId."],"exampleFix":"// before: session rows written without an owner\nawait storage.put({ id: itemId, sessions: { implement: { /* no startedBy */ } } });\n// after: always record the authenticated starter\nawait storage.put({ id: itemId, sessions: { implement: { startedBy: user.workosId } } });","handlingStrategy":"validation","validationCode":"const item = await storage.get({ orgId, id: binding.workItemId });\nconst startedBy = item?.sessions?.[binding.role]?.startedBy;\nif (!startedBy) throw new Error(`work item ${binding.workItemId} lacks startedBy for role ${binding.role}`);","typeGuard":"function hasSessionOwner(binding, item) {\n  return typeof item?.sessions?.[binding.role]?.startedBy === 'string' &&\n    item.sessions[binding.role].startedBy.length > 0;\n}","tryCatchPattern":"try {\n  await dispatcher.dispatch(record);\n} catch (e) {\n  if (String(e?.message).includes('has no authenticated session owner')) {\n    await backfillSessionOwner(record); // re-authenticate and persist startedBy\n  } else throw e;\n}","preventionTips":["Always record startedBy when creating work item sessions — never write session maps without an owner","Keep binding.role keys in sync with session map keys when renaming roles","Backfill startedBy on imported or schema-migrated rows","Validate work items in tests with the same shape used in production"],"tags":["factory","authentication","session","data-integrity"],"backgroundTag":"missing-session-owner","analyzedSha":"75dd419e613fe9c39f846ffc500716141b74fda6","analyzedAt":"2026-08-30T00:15:31.844Z","schemaVersion":2},"datasetVersion":"2026-08-30T03:17:51.788Z"}