{"record":{"id":"f3b8ebcbc8de9428","repo":"CherryHQ/cherry-studio","slug":"path-is-not-a-directory-validpath","errorCode":null,"errorMessage":"Path is not a directory: ${validPath}","messagePattern":"Path is not a directory: (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"warning","filePath":"src/main/ai/mcp/servers/filesystem/tools/glob.ts","lineNumber":49,"sourceCode":"- IMPORTANT: Omit the path field for the default directory (don't use \"undefined\" or \"null\")`,\n  inputSchema: z.toJSONSchema(GlobToolSchema)\n}\n\n// Handler implementation\nexport async function handleGlobTool(args: unknown, baseDir: string) {\n  const parsed = GlobToolSchema.safeParse(args)\n  if (!parsed.success) {\n    throw new Error(`Invalid arguments for glob: ${parsed.error}`)\n  }\n\n  const searchPath = parsed.data.path || baseDir\n  const validPath = await validatePath(searchPath, baseDir)\n\n  // Verify the search directory exists\n  try {\n    const stats = await fs.stat(validPath)\n    if (!stats.isDirectory()) {\n      throw new Error(`Path is not a directory: ${validPath}`)\n    }\n  } catch (error: unknown) {\n    if (error && typeof error === 'object' && 'code' in error && error.code === 'ENOENT') {\n      throw new Error(`Directory not found: ${validPath}`)\n    }\n    throw error\n  }\n\n  // Validate pattern\n  const pattern = parsed.data.pattern.trim()\n  if (!pattern) {\n    throw new Error('Pattern cannot be empty')\n  }\n\n  const files: FileInfo[] = []\n  let truncated = false\n\n  // Build ripgrep arguments for file listing using --glob=pattern format","sourceCodeStart":31,"sourceCodeEnd":67,"githubUrl":"https://github.com/CherryHQ/cherry-studio/blob/726446b54cd69ffe51a276638672f6d95ca0768c/src/main/ai/mcp/servers/filesystem/tools/glob.ts#L31-L67","documentation":"fs.stat on the glob search path succeeded but stats.isDirectory() is false — the caller pointed glob at a regular file or other non-directory. glob requires a directory to search inside. The validated absolute path appears in the message.","triggerScenarios":"Passing a file path (e.g. '/src/foo.ts') as glob's path argument instead of a directory, or pointing at a symlink that resolves to a file.","commonSituations":"A model reusing a file path from a prior read/edit call as the glob search root; confusing the path-to-search with the pattern field.","solutions":["Pass a directory in the path field (or omit it to use the base directory).","To match a specific file, use a pattern that targets it (e.g. '**/foo.ts') against the parent directory.","Run ls on the path first to confirm it is a directory."],"exampleFix":"// before\nif (!stats.isDirectory()) {\n  throw new Error(`Path is not a directory: ${validPath}`)\n}\n\n// after — suggest the parent directory\nconst parent = path.dirname(validPath)\nthrow new Error(`Path is not a directory: ${validPath}. Did you mean to search in ${parent}?`)","handlingStrategy":"validation","validationCode":"// Confirm the search path is a directory before globbing.\nimport { stat } from 'fs/promises'\nasync function assertIsDir(p: string): Promise<void> {\n  const s = await stat(p)\n  if (!s.isDirectory()) throw new Error(`Path is not a directory: ${p}`)\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Pass a directory in the path field, or omit it to use the base directory.","To target a specific file, use a pattern against its parent directory.","Run ls on the path first to verify it is a directory.","Avoid reusing a file path from a previous read/edit as the glob root."],"tags":["filesystem","glob","mcp-tool","type-mismatch"],"backgroundTag":null,"analyzedSha":"726446b54cd69ffe51a276638672f6d95ca0768c","analyzedAt":"2026-08-12T17:30:37.448Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}