{"record":{"id":"2a341c5958d6ca88","repo":"mastra-ai/mastra","slug":"step-this-stepnumber-already-prepared","errorCode":null,"errorMessage":"Step ${this.stepNumber} already prepared","messagePattern":"Step (.+?) already prepared","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/memory/src/processors/observational-memory/observation-turn/step.ts","lineNumber":66,"sourceCode":"  toJSON() {\n    return { stepNumber: this.stepNumber, prepared: this._prepared };\n  }\n\n  /** Step context from prepare(). Throws if prepare() hasn't been called. */\n  get context(): StepContext {\n    if (!this._context) throw new Error('Step not prepared yet — call prepare() first');\n    return this._context;\n  }\n\n  /**\n   * Prepare this step for agent generation.\n   *\n   * For step 0: activates buffered chunks, checks reflection, builds system message, filters observed.\n   * For step > 0: checks thresholds, triggers buffer/observe, saves previous messages,\n   * builds system message, filters observed.\n   */\n  async prepare(): Promise<StepContext> {\n    if (this._prepared) throw new Error(`Step ${this.stepNumber} already prepared`);\n\n    const { threadId, resourceId, messageList } = this.turn;\n    // Cast to any for internal access to private OM methods (Turn/Step are internal consumers)\n    const om = this.turn.om;\n    let activated = false;\n    let observed = false;\n    let buffered = false;\n    let reflected = false;\n    let didThresholdCleanup = false;\n    let observerExchange: StepContext['observerExchange'];\n\n    // ── Step 0: Activate buffered chunks ──────────────────────\n    if (this.stepNumber === 0) {\n      const step0Messages = getObservableMessages(messageList);\n      const activation = await om.activate({\n        threadId,\n        resourceId,\n        checkThreshold: true,","sourceCodeStart":48,"sourceCodeEnd":84,"githubUrl":"https://github.com/mastra-ai/mastra/blob/75dd419e613fe9c39f846ffc500716141b74fda6/packages/memory/src/processors/observational-memory/observation-turn/step.ts#L48-L84","documentation":"Each ObservationStep is single-use: prepare() runs the observation pipeline (threshold checks, buffering, system message building) exactly once. A second prepare() call on the same step would double-apply those effects, so it throws.","triggerScenarios":"Calling `await step.prepare()` twice on the same ObservationStep instance — e.g. a retry wrapper that re-invokes prepare after a partial failure, or code that re-enters generation for the same step number.","commonSituations":"Retry logic around agent generation that re-calls prepare instead of creating a new step; double invocation from both a hook and the main loop; accidental loop reuse of a step variable.","solutions":["Create a new step via `turn.step(n)` (with an incremented step number) and prepare that instead","Restructure retry logic so retries happen inside/after a single prepare, not by re-calling prepare","Track preparation with step.toJSON().prepared before calling prepare()"],"exampleFix":"// before\nawait step.prepare();\nawait step.prepare(); // throws\n// after\nawait step.prepare();\nconst next = turn.step(step.stepNumber + 1);\nawait next.prepare();","handlingStrategy":"type-guard","validationCode":"if (!step.toJSON().prepared) {\n  await step.prepare();\n}","typeGuard":"function canPrepare(step: ObservationStep): boolean {\n  return !step.toJSON().prepared;\n}","tryCatchPattern":"try {\n  await step.prepare();\n} catch (e) {\n  if (e instanceof Error && /already prepared/.test(e.message)) {\n    // reuse existing context instead of re-preparing\n  } else throw e;\n}","preventionTips":["Call prepare() exactly once per step instance","For retries, create a new step with turn.step(n+1) instead of re-preparing","Check toJSON().prepared before invoking prepare"],"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"}