mastra-ai/mastra · error · Error

Turn not started — call start() first

Error message

Turn not started — call start() first

What it means

Turn.record is a cached memory record that is only created during start(). Accessing record before start() means the turn has no loaded record, so the getter throws to enforce the start() -> use lifecycle.

Source

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

    this.threadId = opts.threadId;
    this.resourceId = opts.resourceId;
    this.messageList = opts.messageList;
    this.agent = opts.agent;
    this.sendSignal = opts.sendSignal;
    this.sendStateSignal = opts.sendStateSignal;
    this.requestContext = opts.requestContext;
    this.observabilityContext = opts.observabilityContext;
    this.hooks = opts.hooks ?? {};
  }

  readonly om: ObservationalMemory;
  readonly threadId: string;
  readonly resourceId: string | undefined;
  readonly messageList: MessageList;

  /** The current cached record. Refreshed after mutations (activate/observe/reflect). */
  get record(): ObservationalMemoryRecord {
    if (!this._record) throw new Error('Turn not started — call start() first');
    return this._record;
  }

  /** The context loaded during start(). */
  get context(): TurnContext {
    if (!this._context) throw new Error('Turn not started — call start() first');
    return this._context;
  }

  /** The current step, if one exists. */
  get currentStep(): ObservationStep | undefined {
    return this._currentStep;
  }

  addHooks(hooks?: ObservationTurnHooks): void {
    if (!hooks) return;
    Object.assign(this.hooks, hooks);
  }

View on GitHub (pinned to 75dd419e61)

Solutions

  1. Call `await turn.start(memory, runState)` before reading turn.record
  2. Check the private _started flag / restructure so record access happens in code paths after start()
  3. If you only need the record outside a turn, call om.getOrCreateRecord(threadId, resourceId) directly

Example fix

// before
const turn = new ObservationTurn(om, threadId, resourceId, messageList);
console.log(turn.record); // throws
// after
const turn = new ObservationTurn(om, threadId, resourceId, messageList);
await turn.start();
console.log(turn.record); // ok
Defensive patterns

Strategy: type-guard

Validate before calling

// only access record after start() has resolved
await turn.start(memory);
const rec = turn.record;

Type guard

// ObservationTurn exposes no public started flag; gate by tracking start yourself
let started = false;
function turnStarted(t: ObservationTurn): boolean { return started; }
// set started = true after `await turn.start(...)` resolves

Try / catch

let record: ObservationalMemoryRecord;
try {
  record = turn.record;
} catch (e) {
  if (e instanceof Error && e.message.includes('Turn not started')) {
    await turn.start();
    record = turn.record;
  } else throw e;
}

Prevention

When it happens

Trigger: Reading `turn.record` after constructing `new ObservationTurn(...)` but before `await turn.start(...)` completes.

Common situations: Logging the record at turn setup; a consumer that inspects the record for telemetry before the turn is started; forgetting to await start() (floating promise) and reading record immediately after.

Related errors


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