{"record":{"id":"4768684cb18bd7f7","repo":"CherryHQ/cherry-studio","slug":"invalid-thought-must-be-a-string","errorCode":null,"errorMessage":"Invalid thought: must be a string","messagePattern":"Invalid thought: must be a string","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"warning","filePath":"src/main/ai/mcp/servers/sequentialthinking.ts","lineNumber":33,"sourceCode":"  thoughtNumber: number\n  totalThoughts: number\n  isRevision?: boolean\n  revisesThought?: number\n  branchFromThought?: number\n  branchId?: string\n  needsMoreThoughts?: boolean\n  nextThoughtNeeded: boolean\n}\n\nclass SequentialThinkingServer {\n  private thoughtHistory: ThoughtData[] = []\n  private branches: Record<string, ThoughtData[]> = {}\n\n  private validateThoughtData(input: unknown): ThoughtData {\n    const data = input as Record<string, unknown>\n\n    if (!data.thought || typeof data.thought !== 'string') {\n      throw new Error('Invalid thought: must be a string')\n    }\n    if (!data.thoughtNumber || typeof data.thoughtNumber !== 'number') {\n      throw new Error('Invalid thoughtNumber: must be a number')\n    }\n    if (!data.totalThoughts || typeof data.totalThoughts !== 'number') {\n      throw new Error('Invalid totalThoughts: must be a number')\n    }\n    if (typeof data.nextThoughtNeeded !== 'boolean') {\n      throw new Error('Invalid nextThoughtNeeded: must be a boolean')\n    }\n\n    return {\n      thought: data.thought,\n      thoughtNumber: data.thoughtNumber,\n      totalThoughts: data.totalThoughts,\n      nextThoughtNeeded: data.nextThoughtNeeded,\n      isRevision: data.isRevision as boolean | undefined,\n      revisesThought: data.revisesThought as number | undefined,","sourceCodeStart":15,"sourceCodeEnd":51,"githubUrl":"https://github.com/CherryHQ/cherry-studio/blob/726446b54cd69ffe51a276638672f6d95ca0768c/src/main/ai/mcp/servers/sequentialthinking.ts#L15-L51","documentation":"The sequential-thinking MCP server validates the `thought` field of each `sequentialthinking` call: it must be present and `typeof === 'string'`. It throws a plain `Error` (NOT an McpError). Crucially, `processThought` catches this error itself (line 126) and returns it as a tool result with `isError: true` and a JSON `{ error, status: 'failed' }` body — so the caller never sees a thrown exception, only a failed tool result.","triggerScenarios":"Submitting a thought where `thought` is missing, null, a number, an object, or an empty string (the `!data.thought` guard also rejects `''`).","commonSituations":"The model passes only numeric fields; a client omits the prose; `thought` is templated from an empty variable.","solutions":["Always include a non-empty `thought` string in every call.","Check the returned `isError` flag and `status: 'failed'` body rather than expecting an exception.","Validate the four required fields client-side before calling (thought, thoughtNumber, totalThoughts, nextThoughtNeeded)."],"exampleFix":"// before\n{ thought: \"\", thoughtNumber: 1, totalThoughts: 3, nextThoughtNeeded: true }\n// after\n{ thought: \"Decompose the request into subproblems.\", thoughtNumber: 1, totalThoughts: 3, nextThoughtNeeded: true }","handlingStrategy":"validation","validationCode":"function buildThoughtArgs(raw: unknown) {\n  if (typeof (raw as any)?.thought !== 'string' || (raw as any).thought.length === 0) {\n    throw new TypeError(\"'thought' must be a non-empty string\")\n  }\n  return raw as { thought: string; thoughtNumber: number; totalThoughts: number; nextThoughtNeeded: boolean }\n}","typeGuard":"const hasThought = (v: unknown): v is { thought: string } =>\n  typeof v === 'object' && v !== null && typeof (v as any).thought === 'string' && (v as any).thought.length > 0","tryCatchPattern":"// This server returns soft errors, not thrown ones — check isError on the result.\nconst result = await client.callTool({ name: 'sequentialthinking', arguments: buildThoughtArgs(raw) })\nif (result.isError) {\n  const body = JSON.parse((result.content[0] as any).text)\n  if (/Invalid thought/.test(body.error)) {/* rebuild args with a non-empty thought */}\n}","preventionTips":["Always include a non-empty `thought` string.","Check the tool result's `isError` flag — these errors do not throw.","Validate all four required fields (thought, thoughtNumber, totalThoughts, nextThoughtNeeded) together."],"tags":["mcp","validation","sequential-thinking","soft-error"],"backgroundTag":null,"analyzedSha":"726446b54cd69ffe51a276638672f6d95ca0768c","analyzedAt":"2026-08-12T17:30:37.448Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}