{"record":{"id":"642f8f68357e5dba","repo":"can1357/oh-my-pi","slug":"grep-cannot-recurse-the-directory-listing-at-raw","errorCode":null,"errorMessage":"grep cannot recurse the directory listing at ${rawPath}; grep a specific file under it (e.g. ${rawPath.replace(/\\/+$/, \"\")}/<file>) or read ${rawPath} to list its entries","messagePattern":"grep cannot recurse the directory listing at (.+?); grep a specific file under it \\(e\\.g\\. (.+?)/<file>\\) or read (.+?) to list its entries","errorType":"validation","errorClass":"ToolError","httpStatus":null,"severity":"error","filePath":"packages/coding-agent/src/tools/grep.ts","lineNumber":817,"sourceCode":"\t};\n\n\tfor (let idx = 0; idx < paths.length; idx++) {\n\t\tconst rawPath = paths[idx];\n\t\tif (!rawPath || opts.archiveDisplayMap.has(rawPath) || !internalRouter.canHandle(rawPath)) {\n\t\t\tcontinue;\n\t\t}\n\t\t// `ssh://[::1]/path` carries `[`/`]` in the IPv6 authority — glob metacharacters\n\t\t// — so check only the path portion for ssh:// (the SSH handler reads a single\n\t\t// remote file; there is no glob expansion). A glob in the remote path still trips.\n\t\tconst globTarget = /^ssh:\\/\\//i.test(rawPath) ? rawPath.replace(/^ssh:\\/\\/[^/]*/i, \"\") : rawPath;\n\t\tif (hasGlobPathChars(globTarget)) {\n\t\t\tthrow new ToolError(`Glob patterns are not supported for internal URLs: ${rawPath}`);\n\t\t}\n\t\tlet resource = await internalRouter.resolve(rawPath, context);\n\t\t// A directory listing with no backing local path (e.g. a remote ssh:// dir)\n\t\t// has no real contents to grep — searching its listing text would be\n\t\t// misleading. Local/skill/vault dir resources set `sourcePath` and skip this.\n\t\tif (resource.isDirectory && !resource.sourcePath) {\n\t\t\tthrow new ToolError(\n\t\t\t\t`grep cannot recurse the directory listing at ${rawPath}; grep a specific file under it (e.g. ${rawPath.replace(/\\/+$/, \"\")}/<file>) or read ${rawPath} to list its entries`,\n\t\t\t);\n\t\t}\n\t\tif (resource.sourcePath) {\n\t\t\tpaths[idx] = resource.sourcePath;\n\t\t\tif (resource.immutable) {\n\t\t\t\timmutableSourcePaths.add(path.resolve(resource.sourcePath));\n\t\t\t}\n\t\t\tcontinue;\n\t\t}\n\n\t\t// No sourcePath: this handler needs its content materialized so the\n\t\t// virtual expansion can search it. Re-resolve without pathOnly.\n\t\tif (context.pathOnly) {\n\t\t\tresource = await internalRouter.resolve(rawPath, { ...context, pathOnly: false });\n\t\t}\n","sourceCodeStart":799,"sourceCodeEnd":835,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/coding-agent/src/tools/grep.ts#L799-L835","documentation":"The resolved internal resource is a directory whose listing has no backing local path (sourcePath is unset, e.g. a remote ssh:// directory). grep would otherwise search the rendered listing text, which is misleading, so it refuses and tells you to grep a specific file or read the URL to list entries.","triggerScenarios":"grep with `path` set to an internal directory URL without a local backing store, e.g. `internal://remote/dir/` or `ssh://host/var/data/` where the handler returns a listing only.","commonSituations":"Pointing grep at a remote ssh:// directory expecting recursive search over remote files; local, skill, and vault directories set sourcePath and do NOT trigger this — only listing-only virtual dirs do.","solutions":["Read the directory URL first to enumerate entries, then grep a specific file under it","Append a concrete file name to the directory URL (e.g. ssh://host/dir/file.log)","If the files are also mounted locally, grep the local filesystem path instead"],"exampleFix":"// before\nawait grep({ pattern: \"ERROR\", path: \"ssh://host/var/log/\" });\n// after\nconst listing = await read(\"ssh://host/var/log/\");\nawait grep({ pattern: \"ERROR\", path: \"ssh://host/var/log/syslog\" });","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"try {\n  await grep({ pattern, path: dirUrl });\n} catch (err) {\n  if (err instanceof ToolError && /cannot recurse the directory listing/.test(err.message)) {\n    const listing = await read(dirUrl);\n    // pick a concrete file from listing and grep it\n  } else throw err;\n}","preventionTips":["read the directory URL first to enumerate entries before grepping it","Prefer local/skill/vault dirs (they expose sourcePath and are greppable) over listing-only remote dirs"],"tags":["grep","directory","internal-url"],"backgroundTag":"grep-cannot-recurse-directory","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}