CherryHQ/cherry-studio · error · McpError

ErrorCode.InternalError

ErrorCode.InternalError

Error message

Agent not found: ${this.agentId}

What it means

getAgentDataPath() is the gate every memory write/read must pass. It calls agentService.getAgent(this.agentId) and throws McpError InternalError if the agent no longer exists. The comment on line 175 states this is deliberate: memory writes must stop once the owning agent is gone, preventing orphaned data writes to a deleted agent's directory.

Source

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

            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)) {
      throw new McpError(ErrorCode.InternalError, `Agent data path mismatch for ${this.agentId}`)
    }
    return assertedPath
  }

  private async assertMemoryDirectory(): Promise<string> {
    const agentDataPath = await this.getAgentDataPath()
    const memoryDir = path.join(agentDataPath, 'memory')
    const memoryStat = await lstat(memoryDir)
    if (!memoryStat.isDirectory() || memoryStat.isSymbolicLink()) {
      throw new Error(`Agent memory directory must be a real directory: ${memoryDir}`)
    }
    return memoryDir
  }

  private async assertRegularFileOrMissing(filePath: string): Promise<void> {

View on GitHub (pinned to 726446b54c)

Solutions

  1. Verify the agent still exists before issuing memory tool calls (agentService.getAgent(id)).
  2. Tear down the MCP server when its agent is deleted so no stale calls reach it.
  3. If the agent was deleted intentionally, no action is needed — the error is the correct behavior.

Example fix

// before: server outlives agent
const server = new AgentMemoryServer(deletedAgentId, path)
await server.callTool('memory', { action: 'append', text: '...' }) // throws

// after: check liveness before constructing/calling
if (!agentService.getAgent(agentId)) throw new Error('Agent gone; skip memory write')
const server = new AgentMemoryServer(agentId, path)
Defensive patterns

Strategy: validation

Validate before calling

import { agentService } from '@data/services/AgentService'

// Before constructing or calling AgentMemoryServer:
if (!agentService.getAgent(agentId)) {
  throw new Error(`Cannot use memory: agent ${agentId} no longer exists`)
}

Try / catch

// Recommended: dispose the memory server when its agent is deleted.
agentService.on('agentDeleted', (id) => {
  if (id === memoryServer.agentId) memoryServer.dispose()
})

Prevention

When it happens

Trigger: The agent was deleted via the UI or agentService while an MCP session/tool call was in flight; the agentId passed to the AgentMemoryServer constructor was never valid or pointed to a different agent; a stale MCP server instance outlived its agent.

Common situations: User deletes an agent during an active conversation; a race between agent deletion and an in-flight memory tool call; agent data was migrated or the agent id changed.

Related errors


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