{"record":{"id":"3d2d900c2f929e31","repo":"can1357/oh-my-pi","slug":"cursor-targetmodelid-cannot-continue-history-fr","errorCode":null,"errorMessage":"Cursor ${targetModelId} cannot continue history from a different model (${msg.provider}/${msg.model}); start a new session.","messagePattern":"Cursor (.+?) cannot continue history from a different model \\((.+?)/(.+?)\\); start a new session\\.","errorType":"validation","errorClass":"AIError.ValidationError","httpStatus":null,"severity":"error","filePath":"packages/ai/src/providers/cursor.ts","lineNumber":4785,"sourceCode":"function assertCursorKimiK3HistoryReplayable(\n\tmessages: Message[],\n\tactiveUserMessageIndex: number,\n\ttargetModelId: string | undefined,\n): void {\n\tif (!targetModelId || classifyModel(\"cursor\", targetModelId).family !== \"k3\") return;\n\tconst historyEnd = activeUserMessageIndex >= 0 ? activeUserMessageIndex : messages.length;\n\tconst missingThinkingTurns: number[] = [];\n\tconst newlyWarnedKeys: string[] = [];\n\tlet assistantTurn = 0;\n\tfor (let i = 0; i < historyEnd; i++) {\n\t\tconst msg = messages[i];\n\t\tif (msg.role !== \"assistant\") continue;\n\t\tassistantTurn++;\n\t\tconst isSameCursorModel = msg.api === \"cursor-agent\" && msg.provider === \"cursor\" && msg.model === targetModelId;\n\t\tif (!isSameCursorModel) {\n\t\t\t// Foreign history genuinely cannot replay K3 thinking: another model's\n\t\t\t// turns carry no K3-signed reasoning to reconstruct.\n\t\t\tthrow new AIError.ValidationError(\n\t\t\t\t`Cursor ${targetModelId} cannot continue history from a different model (${msg.provider}/${msg.model}); start a new session.`,\n\t\t\t);\n\t\t}\n\t\tconst hasThinking = msg.content.some(item => item.type === \"thinking\" && item.thinking.length > 0);\n\t\tif (hasThinking) continue;\n\t\tconst warningKey = `${msg.api}\\0${msg.provider}\\0${msg.model}\\0${msg.timestamp}`;\n\t\tif (warnedCursorKimiK3ReplayMessages.has(warningKey)) continue;\n\t\tmissingThinkingTurns.push(assistantTurn);\n\t\tnewlyWarnedKeys.push(warningKey);\n\t}\n\tif (missingThinkingTurns.length === 0) return;\n\tfor (const key of newlyWarnedKeys) warnedCursorKimiK3ReplayMessages.add(key);\n\tlogger.warn(\n\t\t`Cursor kimi-k3 history contains same-model assistant turn(s) ${missingThinkingTurns.join(\", \")} without thinking blocks; replaying those spans without reasoning may make generation less stable`,\n\t\t{ model: targetModelId, assistantTurns: missingThinkingTurns },\n\t);\n}\n","sourceCodeStart":4767,"sourceCodeEnd":4803,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/ai/src/providers/cursor.ts#L4767-L4803","documentation":"Cursor K3 models reconstruct prior assistant turns' signed reasoning from the conversation history. If any prior assistant message was produced by a different model (different provider/model id), there is no K3-signed thinking to replay, so the library throws ValidationError telling you to start a new session instead of silently corrupting context.","triggerScenarios":"Calling the Cursor streaming path with a conversation history that includes assistant messages where msg.api !== \"cursor-agent\" or msg.provider !== \"cursor\" or msg.model !== targetModelId — typically switching models mid-session (e.g. from claude to a cursor K3 model).","commonSituations":"User switches models in the middle of a chat and the client replays full history; a fallback route substituted another provider's assistant turns into the transcript; session persistence across a model upgrade changes the model id string.","solutions":["Start a new session/conversation when switching to a different Cursor K3 model, as the message says.","Filter or truncate history to only assistant turns from the exact same cursor model before calling.","Route the request back to the original model that produced the history if continuation is required.","Adjust session bookkeeping so model changes reset the stored transcript instead of replaying foreign turns."],"exampleFix":"// before\nawait streamCursor(newModel, { ...context, messages: oldMessages });\n// after\nconst sameModelMessages = oldMessages.filter(\n  m => m.role !== \"assistant\" || (m.provider === \"cursor\" && m.model === newModel.id),\n);\nawait streamCursor(newModel, { ...context, messages: sameModelMessages });","handlingStrategy":"validation","validationCode":"const foreign = messages.find(m => m.role === \"assistant\" &&\n  !(m.api === \"cursor-agent\" && m.provider === \"cursor\" && m.model === targetModelId));\nif (foreign) {\n  // start a new session or filter history before calling Cursor\n}","typeGuard":"null","tryCatchPattern":"try {\n  await streamCursor(model, ctx, { messages, ...options });\n} catch (err) {\n  if (err instanceof AIError.ValidationError && err.message.includes(\"cannot continue history from a different model\")) {\n    // reset the session: clear transcript or spin up a new conversation\n  } else throw err;\n}","preventionTips":["Reset conversation transcripts whenever the user switches Cursor models.","Tag assistant messages with provider/model and filter before replay.","Never mix fallback-provider assistant turns into Cursor K3 history."],"tags":["cursor","conversation-history","model-mismatch","validation"],"backgroundTag":"model-history-mismatch","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}