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

  1. Point `path` at a concrete file, not a directory or submodule
  2. List the directory first (github list/dir op or browse the repo) to find the exact file path
  3. 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

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


AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31). Data as JSON: /api/errors/f964f0b0d990b187. Report an issue: GitHub.