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
- Use one of the three exact values: 'update', 'append', 'search' (lowercase).
- Ensure the arguments object includes action as a required field.
- 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
- Validate action against the enum client-side before calling the tool.
- Pass action as a lowercase string; the switch is case-sensitive.
- Provide the full arguments object — do not leave arguments null.
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
- InvalidParams
- InvalidParams
- Invalid command: command must be a non-empty string
- Invalid command: command cannot be empty
- Invalid args: must be an array
AI-assisted analysis of CherryHQ/cherry-studio@726446b54c (2026-08-12).
Data as JSON: /api/errors/a8f4c60e4c1c90cf.
Report an issue: GitHub.