{"record":{"id":"60ac5e936c2ca6c6","repo":"CopilotKit/CopilotKit","slug":"thread-setstate-invalid-state-r-error","errorCode":null,"errorMessage":"thread.setState: invalid state — ${r.error}","messagePattern":"thread\\.setState: invalid state — (.+?)","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/channels-core/src/thread.ts","lineNumber":430,"sourceCode":"\n  /** Returns true if this conversation is currently subscribed. */\n  isSubscribed(): Promise<boolean> {\n    return this.trackOperation(\n      async () =>\n        (await this.store.kv.get<boolean>(\n          `sub:${this.deps.conversationKey}`,\n        )) === true,\n    );\n  }\n\n  /** Persist arbitrary per-thread state (e.g. workflow step). */\n  setState<T>(v: T): Promise<void> {\n    return this.trackOperation(async () => {\n      let value: unknown = v;\n      if (this.deps.stateSchema) {\n        const r = await validateSchema(this.deps.stateSchema, v);\n        if (!r.ok)\n          throw new Error(`thread.setState: invalid state — ${r.error}`);\n        value = r.value;\n      }\n      await this.store.kv.set(\n        `threadstate:${this.deps.conversationKey}`,\n        value,\n      );\n    });\n  }\n\n  /** Read back per-thread state previously written with `setState`. */\n  state<T>(): Promise<T | undefined> {\n    return this.trackOperation(() =>\n      this.store.kv.get<T>(`threadstate:${this.deps.conversationKey}`),\n    );\n  }\n\n  /** Read the conversation's messages (returns `[]` when the adapter can't read history). */\n  getMessages(): Promise<ThreadMessage[]> {","sourceCodeStart":412,"sourceCodeEnd":448,"githubUrl":"https://github.com/CopilotKit/CopilotKit/blob/68fbe97d87420bd84e8196965c71bd6e394a3f7a/packages/channels-core/src/thread.ts#L412-L448","documentation":"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.","triggerScenarios":"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.","commonSituations":"Evolving state shapes without updating the schema (or vice versa); optional fields the schema marks required; state built from unvalidated platform payload data.","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"],"exampleFix":"// before\nconst schema = z.object({ count: z.number() });\nawait thread.setState({ count: \"3\" }); // throws\n\n// after\nawait thread.setState({ count: 3 });","handlingStrategy":"validation","validationCode":"const r = await validateSchema(stateSchema, nextState);\nif (!r.ok) throw new Error(`State invalid: ${r.error}`);\nawait thread.setState(r.value);","typeGuard":null,"tryCatchPattern":"try {\n  await thread.setState(next);\n} catch (e) {\n  if (e instanceof Error && e.message.startsWith(\"thread.setState: invalid state\")) {\n    // parse embedded detail, fix state, retry\n  }\n  throw e;\n}","preventionTips":["Validate with the same schema before setState","Keep stateSchema and state-writing code in the same module so they evolve together"],"tags":["state","setstate","schema","validation","zod"],"backgroundTag":"schema-validation-failed","analyzedSha":"68fbe97d87420bd84e8196965c71bd6e394a3f7a","analyzedAt":"2026-08-27T04:03:26.537Z","schemaVersion":2},"datasetVersion":"2026-08-27T08:17:20.692Z"}