{"record":{"id":"72badbf337c21202","repo":"mastra-ai/mastra","slug":"turn-already-started","errorCode":null,"errorMessage":"Turn already started","messagePattern":"Turn already started","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/memory/src/processors/observational-memory/observation-turn/turn.ts","lineNumber":141,"sourceCode":"\n  /** The current step, if one exists. */\n  get currentStep(): ObservationStep | undefined {\n    return this._currentStep;\n  }\n\n  addHooks(hooks?: ObservationTurnHooks): void {\n    if (!hooks) return;\n    Object.assign(this.hooks, hooks);\n  }\n\n  /**\n   * Load context and cache the record. Call once at the start of the turn.\n   *\n   * If a MemoryContextProvider is passed, loads historical messages and adds\n   * them to the MessageList. Without a provider, only fetches/caches the record.\n   */\n  async start(memory?: MemoryContextProvider, runState?: MemoryRunState): Promise<TurnContext> {\n    if (this._started) throw new Error('Turn already started');\n    this._started = true;\n\n    this._record = await this.om.getOrCreateRecord(this.threadId, this.resourceId);\n    runState?.set(`observational-memory:record:${this.threadId}:${this.resourceId ?? ''}`, this._record);\n    this._generationCountAtStart = this._record.generationCount;\n    this.memory = memory;\n\n    if (memory) {\n      const ctx = await loadMemoryContextMessages({\n        memory,\n        messageList: this.messageList,\n        threadId: this.threadId,\n        resourceId: this.resourceId,\n        runState,\n      });\n\n      this._context = {\n        messages: ctx.messages,","sourceCodeStart":123,"sourceCodeEnd":159,"githubUrl":"https://github.com/mastra-ai/mastra/blob/75dd419e613fe9c39f846ffc500716141b74fda6/packages/memory/src/processors/observational-memory/observation-turn/turn.ts#L123-L159","documentation":"A Turn models one user turn and start() may only run once, since it fetches/caches the record and captures the generation count baseline. A second start() on the same instance would corrupt that baseline, so it throws 'Turn already started'.","triggerScenarios":"Calling `await turn.start()` a second time on the same ObservationTurn — e.g. duplicate start calls from middleware and the main loop, or a retry wrapper that re-invokes start after a failure.","commonSituations":"Retry logic that re-calls start() instead of creating a new turn; double-wiring of memory hooks both calling start; framework version change where the caller now also starts the turn.","solutions":["Create a fresh ObservationTurn for a new start cycle rather than reusing the instance","Ensure only one code path calls start() for a given turn instance","Wrap start() so failures happen before side effects, and on retry construct a new turn"],"exampleFix":"// before\nawait turn.start();\nawait turn.start(memory); // throws\n// after\nawait turn.start(memory);\n// for a retry:\nconst retryTurn = new ObservationTurn(om, threadId, resourceId, messageList);\nawait retryTurn.start(memory);","handlingStrategy":"try-catch","validationCode":"// call start() exactly once per turn instance; track it\nif (!turnStartPromise) turnStartPromise = turn.start(memory, runState);\nawait turnStartPromise;","typeGuard":"// Memoize the start promise so double invocation is impossible\nlet started: Promise<TurnContext> | null = null;\nfunction ensureStarted(t: ObservationTurn): Promise<TurnContext> {\n  return started ??= t.start();\n}","tryCatchPattern":"try {\n  await turn.start(memory, runState);\n} catch (e) {\n  if (e instanceof Error && e.message === 'Turn already started') {\n    // ignore: another path already started this turn\n  } else throw e;\n}","preventionTips":["Memoize the start() promise per turn instance","Ensure only one code path (loop or hook) starts the turn","For retries, construct a new ObservationTurn rather than restarting"],"tags":["observational-memory","lifecycle","idempotency"],"backgroundTag":"method-called-out-of-order","analyzedSha":"75dd419e613fe9c39f846ffc500716141b74fda6","analyzedAt":"2026-08-30T00:15:31.844Z","schemaVersion":2},"datasetVersion":"2026-08-30T03:17:51.788Z"}