CopilotKit/CopilotKit · error · Error
thread.setState: invalid state — ${r.error}
Error message
thread.setState: invalid state — ${r.error} What it means
thread.setState persists conversation state, and when the channel was created with a stateSchema, the value is validated first. If validation fails, the raw error text is embedded in this message so you can see which field violated the schema. The write to the KV store is skipped.
Source
Thrown at packages/channels-core/src/thread.ts:430
/** Returns true if this conversation is currently subscribed. */
isSubscribed(): Promise<boolean> {
return this.trackOperation(
async () =>
(await this.store.kv.get<boolean>(
`sub:${this.deps.conversationKey}`,
)) === true,
);
}
/** Persist arbitrary per-thread state (e.g. workflow step). */
setState<T>(v: T): Promise<void> {
return this.trackOperation(async () => {
let value: unknown = v;
if (this.deps.stateSchema) {
const r = await validateSchema(this.deps.stateSchema, v);
if (!r.ok)
throw new Error(`thread.setState: invalid state — ${r.error}`);
value = r.value;
}
await this.store.kv.set(
`threadstate:${this.deps.conversationKey}`,
value,
);
});
}
/** Read back per-thread state previously written with `setState`. */
state<T>(): Promise<T | undefined> {
return this.trackOperation(() =>
this.store.kv.get<T>(`threadstate:${this.deps.conversationKey}`),
);
}
/** Read the conversation's messages (returns `[]` when the adapter can't read history). */
getMessages(): Promise<ThreadMessage[]> {View on GitHub (pinned to 68fbe97d87)
Solutions
- Read the embedded ${r.error} detail to find the failing field, then fix the state object
- Update stateSchema to match the actual state shape if the schema is stale
- Validate state with the same schema (e.g. zod safeParse) before calling setState
Example fix
// before
const schema = z.object({ count: z.number() });
await thread.setState({ count: "3" }); // throws
// after
await thread.setState({ count: 3 }); Defensive patterns
Strategy: validation
Validate before calling
const r = await validateSchema(stateSchema, nextState);
if (!r.ok) throw new Error(`State invalid: ${r.error}`);
await thread.setState(r.value); Try / catch
try {
await thread.setState(next);
} catch (e) {
if (e instanceof Error && e.message.startsWith("thread.setState: invalid state")) {
// parse embedded detail, fix state, retry
}
throw e;
} Prevention
- Validate with the same schema before setState
- Keep stateSchema and state-writing code in the same module so they evolve together
When it happens
Trigger: Calling thread.setState({...}) with an object missing required fields, wrong field types, or extra fields (if the schema forbids them) after passing stateSchema to createChannel.
Common situations: Evolving state shapes without updating the schema (or vice versa); optional fields the schema marks required; state built from unvalidated platform payload data.
Understand the failure class
Background: Schema validation failed / invalid input schema: payload rejected because its shape doesn't match the expected schema — this error's family across 28 libraries.
Related errors
- Array type must have items property
- Invalid JSON schema
- channel_identity_invalid
- channel_memory_grant_invalid
- Invalid duration: ${input}
AI-assisted analysis of CopilotKit/CopilotKit@68fbe97d87 (2026-08-27).
Data as JSON: /api/errors/60ac5e936c2ca6c6.
Report an issue: GitHub.