mastra-ai/mastra · error

Experiment emitted multiple terminal semantic events

Error message

Experiment emitted multiple terminal semantic events

What it means

During an experiment run, the CLI processes semantic events from the experiment stream and expects exactly one terminal event ('experiment.run.finished'). This error is thrown when a second terminal event arrives after one has already been recorded, indicating the experiment runtime emitted a malformed or duplicated lifecycle stream.

Source

Thrown at packages/cli/src/commands/experiment/runtime.ts:581

            tenant: packet.tenant,
            environment: packet.environment,
            artifacts: packet.artifacts,
            scorerVersions: Object.fromEntries(packet.scorers.map(scorer => [scorer.id, scorer.version])),
            policies: packet.policies,
            requestedAt: packet.requestedAt,
          },
          persistence: { experiments: 'none', scores: 'none' },
          experimentId: request.experimentId,
          onEvent: async event => {
            if (event.type === 'experiment.run.started') await writeEvent('run-started', { semanticEvent: event });
            else if (event.type === 'experiment.item.completed') {
              await writeEvent('item-completed', {
                itemId: event.itemId,
                itemIndex: event.itemIndex,
                semanticEvent: event,
              });
            } else if (event.type === 'experiment.run.finished') {
              if (finishedEvent) throw new Error('Experiment emitted multiple terminal semantic events');
              finishedEvent = event;
            }
          },
        }),
        deadlinePromise,
      ]);
    } catch (error) {
      runErrorRetryable = isRecord(error) && error.retryable === true;
      runError = error instanceof Error ? error : new Error(String(error));
    } finally {
      clearDeadlineTimer?.();
      clearDeadlineTimer = undefined;
    }
    try {
      let clearTimer: (() => void) | undefined;
      await Promise.race([
        mastra.shutdown(),
        new Promise((_, reject) => {

View on GitHub (pinned to 75dd419e61)

Solutions

  1. Inspect the experiment runner for duplicate emission of 'experiment.run.finished' (e.g. both a normal-completion path and a deadline path emitting the event).
  2. Guard the deadline path so it does not emit a terminal event when the run already finished, or abort the stream first.
  3. If events may be redelivered by the transport, make the handler idempotent (ignore or log subsequent terminal events instead of throwing) if duplicate tolerance is acceptable.
  4. Update to the latest @mastra/cli / core versions in case this is a known runtime duplication bug.

Example fix

// before
} else if (event.type === 'experiment.run.finished') {
  if (finishedEvent) throw new Error('Experiment emitted multiple terminal semantic events');
  finishedEvent = event;
}
// after
} else if (event.type === 'experiment.run.finished') {
  if (finishedEvent) {
    logger.warn('Ignoring duplicate terminal semantic event');
    return;
  }
  finishedEvent = event;
}
Defensive patterns

Strategy: try-catch

Try / catch

try {
  await executeRun(...);
} catch (err) {
  if (err.message === 'Experiment emitted multiple terminal semantic events') {
    // treat last terminal event as authoritative, or log and re-run with dedup
  }
}

Prevention

When it happens

Trigger: Calling executeRun with a run whose event stream emits two 'experiment.run.finished' events (e.g. a race between the run completing and the deadline/timeout path also finalizing, or a runtime bug emitting duplicate finish events).

Common situations: Running experiments against a runtime that redelivers events, deadlines firing concurrently with normal completion, or a runner that retries and finishes the run twice without a new stream.

Related errors


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