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
- Call `await step.prepare()` before reading `step.context`
- Check `step.toJSON().prepared` (or wrap access) if you need to read context conditionally
- 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
- Always await step.prepare() before reading context
- Never log/inspect step.context in pre-prepare code paths
- Track prepare() calls to avoid floating promises
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
- Turn not started — call start() first
- No active thread on this session
- No source thread to clone
- Mode not found: ${this.#id}
- Observational memory record not found: ${id}
AI-assisted analysis of mastra-ai/mastra@75dd419e61 (2026-08-30).
Data as JSON: /api/errors/9ff32bba526421ed.
Report an issue: GitHub.