CherryHQ/cherry-studio · warning · Error

Invalid thought: must be a string

Error message

Invalid thought: must be a string

What it means

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.

Source

Thrown at src/main/ai/mcp/servers/sequentialthinking.ts:33

  thoughtNumber: number
  totalThoughts: number
  isRevision?: boolean
  revisesThought?: number
  branchFromThought?: number
  branchId?: string
  needsMoreThoughts?: boolean
  nextThoughtNeeded: boolean
}

class SequentialThinkingServer {
  private thoughtHistory: ThoughtData[] = []
  private branches: Record<string, ThoughtData[]> = {}

  private validateThoughtData(input: unknown): ThoughtData {
    const data = input as Record<string, unknown>

    if (!data.thought || typeof data.thought !== 'string') {
      throw new Error('Invalid thought: must be a string')
    }
    if (!data.thoughtNumber || typeof data.thoughtNumber !== 'number') {
      throw new Error('Invalid thoughtNumber: must be a number')
    }
    if (!data.totalThoughts || typeof data.totalThoughts !== 'number') {
      throw new Error('Invalid totalThoughts: must be a number')
    }
    if (typeof data.nextThoughtNeeded !== 'boolean') {
      throw new Error('Invalid nextThoughtNeeded: must be a boolean')
    }

    return {
      thought: data.thought,
      thoughtNumber: data.thoughtNumber,
      totalThoughts: data.totalThoughts,
      nextThoughtNeeded: data.nextThoughtNeeded,
      isRevision: data.isRevision as boolean | undefined,
      revisesThought: data.revisesThought as number | undefined,

View on GitHub (pinned to 726446b54c)

Solutions

  1. Always include a non-empty `thought` string in every call.
  2. Check the returned `isError` flag and `status: 'failed'` body rather than expecting an exception.
  3. Validate the four required fields client-side before calling (thought, thoughtNumber, totalThoughts, nextThoughtNeeded).

Example fix

// before
{ thought: "", thoughtNumber: 1, totalThoughts: 3, nextThoughtNeeded: true }
// after
{ thought: "Decompose the request into subproblems.", thoughtNumber: 1, totalThoughts: 3, nextThoughtNeeded: true }
Defensive patterns

Strategy: validation

Validate before calling

function buildThoughtArgs(raw: unknown) {
  if (typeof (raw as any)?.thought !== 'string' || (raw as any).thought.length === 0) {
    throw new TypeError("'thought' must be a non-empty string")
  }
  return raw as { thought: string; thoughtNumber: number; totalThoughts: number; nextThoughtNeeded: boolean }
}

Type guard

const hasThought = (v: unknown): v is { thought: string } =>
  typeof v === 'object' && v !== null && typeof (v as any).thought === 'string' && (v as any).thought.length > 0

Try / catch

// This server returns soft errors, not thrown ones — check isError on the result.
const result = await client.callTool({ name: 'sequentialthinking', arguments: buildThoughtArgs(raw) })
if (result.isError) {
  const body = JSON.parse((result.content[0] as any).text)
  if (/Invalid thought/.test(body.error)) {/* rebuild args with a non-empty thought */}
}

Prevention

When it happens

Trigger: Submitting a thought where `thought` is missing, null, a number, an object, or an empty string (the `!data.thought` guard also rejects `''`).

Common situations: The model passes only numeric fields; a client omits the prose; `thought` is templated from an empty variable.

Related errors


AI-assisted analysis of CherryHQ/cherry-studio@726446b54c (2026-08-12). Data as JSON: /api/errors/4768684cb18bd7f7. Report an issue: GitHub.