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
- Remove the `transient` property entirely from state signal inputs (even `transient: false` triggers the error).
- Omit the key when spreading: `const { transient: _t, ...rest } = input; createSignal({ ...rest, type: 'state' })`.
- If you need delivery-only behavior, use a non-state signal type instead of state+transient.
- 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
- Never spread raw input objects into createSignal; destructure and drop `transient` for state signals
- Remember `transient: false` still triggers the error — omit the key entirely
- Model state-signal inputs with a type that omits `transient`
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
- state signal id is required
- state signal cacheKey is required
- tool_result must be preceded by a tool_call
- Could not create the session. Reload the page and try again.
- crossProcessPubSub requires a pubsub instance
AI-assisted analysis of mastra-ai/mastra@75dd419e61 (2026-08-30).
Data as JSON: /api/errors/950b43ab80643a89.
Report an issue: GitHub.