CherryHQ/cherry-studio · error · McpError

ErrorCode.InvalidParams

ErrorCode.InvalidParams

Error message

Unknown action "${action}", expected update/append/search

What it means

Inside the 'memory' tool handler, the 'action' argument is switched against update/append/search. Any other value (including undefined) falls to the default branch and throws McpError with ErrorCode.InvalidParams. The tool's inputSchema declares action as required with an enum of the three values, so a well-behaved client is pre-validated.

Source

Thrown at src/main/ai/mcp/servers/agentMemory.ts:161

    this.mcpServer.server.setRequestHandler(CallToolRequestSchema, async (request) => {
      const toolName = request.params.name
      const args = (request.params.arguments ?? {}) as Record<string, string | undefined>

      try {
        if (toolName !== 'memory') {
          throw new McpError(ErrorCode.MethodNotFound, `Unknown tool: ${toolName}`)
        }
        const action = args.action
        switch (action) {
          case 'update':
            return await this.memoryUpdate(args)
          case 'append':
            return await this.memoryAppend(args)
          case 'search':
            return await this.memorySearch(args)
          default:
            throw new McpError(ErrorCode.InvalidParams, `Unknown action "${action}", expected update/append/search`)
        }
      } catch (error) {
        const message = error instanceof Error ? error.message : String(error)
        logger.error(`Tool error: ${toolName}`, { agentId: this.agentId, error: message })
        return {
          content: [{ type: 'text' as const, text: `Error: ${message}` }],
          isError: true
        }
      }
    })
  }

  private async getAgentDataPath(): Promise<string> {
    // Deliberate existence check: memory writes must stop once the owning agent is gone.
    const agent = agentService.getAgent(this.agentId)
    if (!agent) throw new McpError(ErrorCode.InternalError, `Agent not found: ${this.agentId}`)
    const assertedPath = await assertAgentDataDirectory(path.dirname(this.agentDataPath), this.agentId)
    if (path.resolve(assertedPath) !== path.resolve(this.agentDataPath)) {

View on GitHub (pinned to 726446b54c)

Solutions

  1. Use one of the three exact values: 'update', 'append', 'search' (lowercase).
  2. Ensure the arguments object includes action as a required field.
  3. Validate client-side against the tool's inputSchema before calling.

Example fix

// before
await callTool('memory', { action: 'read', query: 'facts' })

// after
await callTool('memory', { action: 'search', query: 'facts' })
Defensive patterns

Strategy: validation

Validate before calling

const MEMORY_ACTIONS = new Set(['update', 'append', 'search'])
function validateMemoryArgs(args: unknown): asserts args is { action: 'update' | 'append' | 'search'; [k: string]: unknown } {
  const a = (args as { action?: string })?.action
  if (!a || !MEMORY_ACTIONS.has(a)) {
    throw new Error(`action must be one of update/append/search, got: ${a}`)
  }
}

Type guard

function isMemoryAction(v: unknown): v is 'update' | 'append' | 'search' {
  return v === 'update' || v === 'append' || v === 'search'
}

Prevention

When it happens

Trigger: Client calls the memory tool with action missing, empty, or set to a value like 'read', 'delete', 'clear', or 'UPDATE' (case-sensitive). Also fires if arguments is null and args.action becomes undefined.

Common situations: An LLM hallucinates a non-existent action; a client bypasses schema validation; case sensitivity trips up callers ('Update' vs 'update').

Related errors


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