{"record":{"id":"e36a88d5b46d49d6","repo":"vercel/ai","slug":"not-a-directory-inputpath","errorCode":null,"errorMessage":"Not a directory: ${inputPath}","messagePattern":"Not a directory: (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/harness-cline/src/cline-remote-ops.ts","lineNumber":384,"sourceCode":"      const result = await runShell({\n        command: [\n          `if [ ! -e ${shellQuote(\n            target,\n          )} ]; then echo \"__CLINE_LS_NOT_FOUND__\"; exit 2; fi`,\n          `if [ ! -d ${shellQuote(\n            target,\n          )} ]; then echo \"__CLINE_LS_NOT_DIR__\"; exit 3; fi`,\n          `cd ${shellQuote(target)}`,\n          'ls -1Ap',\n        ].join('; '),\n      });\n\n      const output = result.output.trim();\n      if (output.includes('__CLINE_LS_NOT_FOUND__')) {\n        throw new Error(`Path not found: ${inputPath}`);\n      }\n      if (output.includes('__CLINE_LS_NOT_DIR__')) {\n        throw new Error(`Not a directory: ${inputPath}`);\n      }\n\n      return output\n        .split('\\n')\n        .filter(Boolean)\n        .map(line => line.replace(/[*=@|]$/, ''))\n        .sort((left, right) =>\n          left.toLowerCase().localeCompare(right.toLowerCase()),\n        )\n        .slice(0, limit);\n    },\n  };\n}\n","sourceCodeStart":366,"sourceCodeEnd":398,"githubUrl":"https://github.com/vercel/ai/blob/69428b1f8b037e4d118fb4853428d5c4e620493c/packages/harness-cline/src/cline-remote-ops.ts#L366-L398","documentation":"Thrown by ls when the path exists but is not a directory: the shell probe echoes __CLINE_LS_NOT_DIR__ (`[ ! -d target ]`, exit 3) and the library throws 'Not a directory'. ls only lists directories, so file paths must be listed via their parent directory instead.","triggerScenarios":"ops.ls('src/index.ts') — passing a file path to ls; ls on a symlink pointing to a file; agents confusing file and directory paths while exploring the workspace.","commonSituations":"Agent tool loops that ls a known file to 'inspect' it; following a symlinked path that resolves to a file; scripted calls that pass whatever path the model produced without checking its type.","solutions":["List the parent directory instead: ops.ls('src') rather than ops.ls('src/index.ts').","Use ops.readFile(path) when the goal is the file's content, not a listing.","Check the path type first via ops.bash(`test -d ${path} && echo dir || echo file`).","Resolve symlinks (ops.bash 'readlink -f <path>') to see what the path actually points to."],"exampleFix":"// before: ls a file\nawait ops.ls('src/index.ts'); // Not a directory: src/index.ts\n\n// after: read the file, list the dir\nconst content = await ops.readFile('src/index.ts');\nconst entries = await ops.ls('src');","handlingStrategy":"validation","validationCode":"async function assertIsDirectory(ops, dir) {\n  const r = await ops.bash(`test -d ${JSON.stringify(dir)} && echo dir || echo notdir`);\n  if (!r.output.includes('dir')) throw new Error(`not a directory: ${dir}`);\n}","typeGuard":null,"tryCatchPattern":"try {\n  return await ops.ls(dir);\n} catch (e) {\n  if (e instanceof Error && e.message.startsWith('Not a directory:')) {\n    return ops.ls(path.posix.dirname(dir)); // list the parent instead\n  }\n  throw e;\n}","preventionTips":["Only pass directory paths to ls; use readFile for file contents.","Check path type with `test -d` when the model-supplied path is ambiguous.","Resolve symlinks with readlink -f to see the real target type.","List the parent directory when you only know a file path."],"tags":["ls","filesystem","not-a-directory"],"backgroundTag":"not-a-directory","analyzedSha":"69428b1f8b037e4d118fb4853428d5c4e620493c","analyzedAt":"2026-08-30T12:32:21.016Z","schemaVersion":2},"datasetVersion":"2026-08-30T13:17:10.514Z"}