{"record":{"id":"3a77c243edfa2499","repo":"can1357/oh-my-pi","slug":"path-is-not-a-directory-target-searchpath","errorCode":null,"errorMessage":"Path is not a directory: ${target.searchPath}","messagePattern":"Path is not a directory: (.+?)","errorType":"validation","errorClass":"ToolError","httpStatus":null,"severity":"error","filePath":"packages/coding-agent/src/tools/glob.ts","lineNumber":434,"sourceCode":"\t\t\t\t\ttry {\n\t\t\t\t\t\tstat = await this.#stat(target.searchPath);\n\t\t\t\t\t} catch (err) {\n\t\t\t\t\t\t// ENAMETOOLONG can never name a real target; surface a clean\n\t\t\t\t\t\t// \"Path not found\" instead of leaking the raw errno (issue #7597).\n\t\t\t\t\t\tif (isEnoent(err) || hasFsCode(err, \"ENAMETOOLONG\")) {\n\t\t\t\t\t\t\tif (isSingle) throw new ToolError(`Path not found: ${scopePath}`);\n\t\t\t\t\t\t\treturn { target, result: [] };\n\t\t\t\t\t\t}\n\t\t\t\t\t\tthrow err;\n\t\t\t\t\t}\n\t\t\t\t\tif (!target.hasGlob && stat.isFile()) {\n\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\ttarget,\n\t\t\t\t\t\t\tresult: [{ path: formatScopePath(target.searchPath), mtime: stat.mtimeMs }],\n\t\t\t\t\t\t};\n\t\t\t\t\t}\n\t\t\t\t\tif (!stat.isDirectory()) {\n\t\t\t\t\t\tif (isSingle) throw new ToolError(`Path is not a directory: ${target.searchPath}`);\n\t\t\t\t\t\treturn { target, result: [] };\n\t\t\t\t\t}\n\t\t\t\t\treturn { target };\n\t\t\t\t}),\n\t\t\t);\n\t\t\tconst nativeScanPending = preparedTargets.some(prepared => prepared.result === undefined);\n\t\t\tif (nativeScanPending && preparationController && signal) {\n\t\t\t\tsignal.removeEventListener(\"abort\", abortPreparation);\n\t\t\t}\n\t\t\tthrowIfAborted(signal);\n\n\t\t\tconst onUpdateMatches: string[] = [];\n\t\t\tconst onUpdateMtimes: number[] = [];\n\t\t\tconst updateIntervalMs = 200;\n\t\t\tlet lastUpdate = 0;\n\t\t\tconst emitUpdate = () => {\n\t\t\t\tif (!onUpdate) return;\n\t\t\t\tconst now = Date.now();","sourceCodeStart":416,"sourceCodeEnd":452,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/coding-agent/src/tools/glob.ts#L416-L452","documentation":"After stat succeeds, execute() verifies the target is a directory (unless the glob itself matched a file). A single-target call whose searchPath is a regular file, socket, fifo, etc. throws 'Path is not a directory' because recursive globbing requires a directory root.","triggerScenarios":"Passing a file path as the glob search root with a pattern that does not match that exact file; passing /dev/null, a symlink to a file, or a special file.","commonSituations":"Confusing the glob tool's signature with grep-like tools that accept files; variable holding a file path reused where a directory was expected; symlink targets changed.","solutions":["Pass a directory as the search path","If you intend a single file, omit the glob pattern so the file-match branch returns it directly","Check fs stat isDirectory() before calling"],"exampleFix":"// before\nawait globTool.execute({ pattern: \"**/*.ts\", path: \"src/index.ts\" })\n// after\nawait globTool.execute({ pattern: \"**/*.ts\", path: \"src\" })","handlingStrategy":"validation","validationCode":"const st = await fs.stat(p);\nif (!st.isDirectory()) throw new Error(`Expected a directory: ${p}`);\nawait globTool.execute({ pattern, path: p });","typeGuard":"async function isDirectory(p: string): Promise<boolean> {\n  return (await fs.stat(p).catch(() => null))?.isDirectory() ?? false;\n}","tryCatchPattern":"try {\n  return await globTool.execute({ pattern, path });\n} catch (err) {\n  if (err instanceof ToolError && err.message.startsWith(\"Path is not a directory:\")) {\n    // treat as single-file match or fix the caller's path variable\n  }\n  throw err;\n}","preventionTips":["Confirm with fs.stat().isDirectory() before using a path as a glob root","Don't reuse file-path variables for directory parameters","Remember: to glob a single file, omit the pattern rather than pointing the root at the file"],"tags":["filesystem","validation","glob-tool"],"backgroundTag":"not-a-directory","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}