can1357/oh-my-pi · error · ToolError
GitHub path '${filePath}' is not a file.
Error message
GitHub path '${filePath}' is not a file. What it means
After fetching the GitHub contents endpoint, executeFileRead verifies the response is actually a file (isGitHubContentsFile). GitHub returns a directory listing (array) for folders and different shapes for symlinks/submodules; anything else means the path is not a readable file, so the tool throws instead of returning unusable data.
Source
Thrown at packages/coding-agent/src/tools/gh.ts:294
"api",
...ghApiHostArgs(ref),
`/repos/${ref.slug}/contents/${endpointPath}`,
"--method",
"GET",
"-H",
"Accept: application/vnd.github+json",
"-H",
"Accept-Encoding: identity",
];
if (branch) {
args.push("-f", `ref=${branch}`);
}
const response = await github.json<GitHubContentsResponse>(session.cwd, args, signal, {
repoProvided: true,
trimOutput: false,
});
if (!isGitHubContentsFile(response)) {
throw new ToolError(`GitHub path '${filePath}' is not a file.`);
}
// A host-less ref went to gh's default host, so the link has to match it.
const fallbackHost = ref.host ?? defaultGhHost();
const fallbackSourceUrl = `https://${fallbackHost}/${ref.slug}/blob/${encodeURIComponent(branch ?? "HEAD")}/${endpointPath}`;
const sourceUrl = response.html_url || fallbackSourceUrl;
if (response.encoding !== "base64" || typeof response.content !== "string") {
const size =
typeof response.size === "number" && response.size >= 0 ? formatBytes(response.size) : "unknown size";
return buildTextResult(
`[GitHub did not return file bytes for '${filePath}' (${size}). Open ${sourceUrl} to view it.]`,
sourceUrl,
{ repo, branch },
);
}
const encoded = response.content.replaceAll(/\s/g, "");
const bytes = Buffer.from(encoded, "base64");View on GitHub (pinned to 9690622007)
Solutions
- Point `path` at a concrete file, not a directory or submodule
- List the directory first (github list/dir op or browse the repo) to find the exact file path
- Check the path and branch spelling — the path may resolve to something unexpected on the chosen ref
Example fix
// before
await executeFileRead({ path: "src" });
// after
await executeFileRead({ path: "src/index.ts" }); Defensive patterns
Strategy: type-guard
Validate before calling
// Preflight: ensure the path is not obviously a directory by checking the tree listing first if available,
// or require the path to contain an extension heuristically
if (!path.includes(".")) console.warn(`'${path}' may be a directory, not a file`); Type guard
function isFileContents(r: unknown): r is GitHubContentsFile {
return typeof r === "object" && r !== null && "type" in r && (r as { type: string }).type === "file";
} Try / catch
try {
const result = await gh.readFile({ path, ref });
} catch (err) {
if (err instanceof ToolError && /is not a file/.test(err.message)) {
// fall back to listing the parent directory or fetching the dir listing
} else throw err;
} Prevention
- Confirm the path points at a blob, not a directory or submodule, before reading
- Verify path+ref combination in the repo browser first
- Handle submodule paths via their gitmodule URL instead of contents API
When it happens
Trigger: Requesting a path that is a directory (e.g. 'src'), a git submodule entry, or an otherwise non-blob contents response; the response shape fails the isGitHubContentsFile check.
Common situations: Pointing the file-read at a folder by mistake; a path that is a submodule in the repo; ref/branch drift where the path became a directory; typos resolving to a top-level directory.
Related errors
- path must be repository-relative
- cannot access {}: Not a directory
- invalid template, {}; with --tmpdir, it may not be absolute
- cannot overwrite directory {0} with non-directory
- cannot overwrite non-directory {1} with directory {0}
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/f964f0b0d990b187.
Report an issue: GitHub.