mastra-ai/mastra · error

state signals cannot be transient

Error message

state signals cannot be transient

What it means

State signals carry cross-turn tracking (version, cacheKey, activeCopies) that is rebuilt from persisted history. Marking a state signal `transient` (delivery-only, not persisted) would silently break that dedupe/tracking because the tracking would point at messages that were never stored. createSignal therefore rejects the combination up front.

Source

Thrown at packages/core/src/agent/signals.ts:590

  if (!input || typeof input !== 'object' || Array.isArray(input)) return false;

  const candidate = input as Partial<CreatedAgentSignal>;
  return candidate.__isCreatedSignal === true;
}

export function createSignal(
  input: Extract<AgentSignalInput, { type: 'state' }>,
): Extract<CreatedAgentSignal, { type: 'state' }>;
export function createSignal(
  input: Extract<AgentSignalInput, { type: Exclude<AgentSignalType, 'state'> }>,
): Extract<CreatedAgentSignal, { type: Exclude<AgentSignalCategory, 'state'> }>;
export function createSignal(input: AgentSignalInput): CreatedAgentSignal;
export function createSignal(input: AgentSignalInput): CreatedAgentSignal {
  if (input.type === 'state' && input.transient !== undefined) {
    // State signals maintain cross-turn tracking (version/cacheKey/activeCopies) that is
    // rebuilt from persisted history — a delivery-only state signal would silently break
    // dedupe and leave tracking pointing at messages that were never stored.
    throw new Error('state signals cannot be transient');
  }
  const signal = normalizeSignal(input);
  const parts = contentsToSignalParts(signal.contents);

  const created = {
    ...signal,
    __isCreatedSignal: true as const,
    toDBMessage: (options?: { threadId?: string; resourceId?: string }) => signalToDBMessage(signal, parts, options),
    toLLMMessage: () => signalToLLMMessage(signal, parts),
    toDataPart: () => signalToDataPart(signal, parts),
  };

  if (created.type === 'state') {
    const { transient: _transient, ...stateSignal } = created;
    return { ...stateSignal, type: created.type };
  }

  return { ...created, type: created.type };

View on GitHub (pinned to 75dd419e61)

Solutions

  1. Remove the `transient` property entirely from state signal inputs (even `transient: false` triggers the error).
  2. Omit the key when spreading: `const { transient: _t, ...rest } = input; createSignal({ ...rest, type: 'state' })`.
  3. If you need delivery-only behavior, use a non-state signal type instead of state+transient.
  4. Sanitize persisted/external payloads in mastraDBMessageToSignal / dataPartToSignal paths before reconstructing.

Example fix

// before
createSignal({ type: 'state', cacheKey: 'docs', transient: false, contents });
// after
createSignal({ type: 'state', cacheKey: 'docs', contents });
Defensive patterns

Strategy: validation

Validate before calling

if (input.type === 'state' && 'transient' in input) {
  throw new TypeError('state signals must not include a transient property');
}

Type guard

type StateSignalInput = Omit<Extract<AgentSignalInput, { type: 'state' }>, 'transient'>;
function isStateWithoutTransient(x: { type: string } & Record<string, unknown>): x is StateSignalInput {
  return x.type === 'state' && !('transient' in x);
}

Try / catch

try {
  signal = createSignal(input);
} catch (e) {
  if (e instanceof Error && e.message === 'state signals cannot be transient') {
    const { transient: _t, ...rest } = input;
    signal = createSignal(rest);
  } else throw e;
}

Prevention

When it happens

Trigger: Calling createSignal (directly or via createStateSignalInput / signal helpers) with `{ type: 'state', transient: ... }` — including `transient: false` — which still counts as `transient !== undefined`.

Common situations: Copying a transient reactive-signal factory and changing only `type` to 'state'; spreading user-supplied options that happen to include a `transient` key; passing `transient: false` thinking it means 'not transient'.

Related errors


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