mastra-ai/mastra · error · Error

Step not prepared yet — call prepare() first

Error message

Step not prepared yet — call prepare() first

What it means

ObservationStep lazily holds a StepContext that only exists after prepare() has run. The `context` getter is a fail-fast accessor: if you read it before calling prepare(), there is nothing to return, so it throws telling you the required call order.

Source

Thrown at packages/memory/src/processors/observational-memory/observation-turn/step.ts:54

    return this._prepared;
  }

  /**
   * Serialize to a minimal, acyclic snapshot.
   *
   * The `turn` back-reference exists only so a step can read context off its parent turn at
   * runtime. It closes the `ObservationTurn._currentStep -> ObservationStep.turn` cycle, so
   * serializing it throws "Converting circular structure to JSON" (e.g. when a turn is stashed
   * in processor state that flows into a processor-workflow snapshot). The parent turn fully
   * owns the step, so omitting the back-reference is lossless.
   */
  toJSON() {
    return { stepNumber: this.stepNumber, prepared: this._prepared };
  }

  /** Step context from prepare(). Throws if prepare() hasn't been called. */
  get context(): StepContext {
    if (!this._context) throw new Error('Step not prepared yet — call prepare() first');
    return this._context;
  }

  /**
   * Prepare this step for agent generation.
   *
   * For step 0: activates buffered chunks, checks reflection, builds system message, filters observed.
   * For step > 0: checks thresholds, triggers buffer/observe, saves previous messages,
   * builds system message, filters observed.
   */
  async prepare(): Promise<StepContext> {
    if (this._prepared) throw new Error(`Step ${this.stepNumber} already prepared`);

    const { threadId, resourceId, messageList } = this.turn;
    // Cast to any for internal access to private OM methods (Turn/Step are internal consumers)
    const om = this.turn.om;
    let activated = false;
    let observed = false;

View on GitHub (pinned to 75dd419e61)

Solutions

  1. Call `await step.prepare()` before reading `step.context`
  2. Check `step.toJSON().prepared` (or wrap access) if you need to read context conditionally
  3. Ensure prepare()'s promise is awaited — an unawaited prepare means context is still undefined when read

Example fix

// before
const step = turn.step(0);
console.log(step.context); // throws
// after
const step = turn.step(0);
const ctx = await step.prepare();
console.log(step.context); // ok
Defensive patterns

Strategy: try-catch

Validate before calling

// guard via the step's own state before access
if (!step.toJSON().prepared) await step.prepare();
const ctx = step.context;

Type guard

function isStepPrepared(step: ObservationStep): boolean {
  return step.toJSON().prepared;
}

Try / catch

let ctx: StepContext;
try {
  ctx = step.context;
} catch (e) {
  if (e instanceof Error && e.message.includes('not prepared yet')) {
    ctx = await step.prepare();
  } else throw e;
}

Prevention

When it happens

Trigger: Accessing `step.context` (or indirectly via code that reads the step context) before `await step.prepare()` has completed for that step instance.

Common situations: Calling context in a derived getter/log statement before generation; forgetting to await prepare() (floating promise); reusing an old step object from a previous turn whose prepare was never invoked.

Related errors


AI-assisted analysis of mastra-ai/mastra@75dd419e61 (2026-08-30). Data as JSON: /api/errors/9ff32bba526421ed. Report an issue: GitHub.