{"record":{"id":"f964f0b0d990b187","repo":"can1357/oh-my-pi","slug":"github-path-filepath-is-not-a-file","errorCode":null,"errorMessage":"GitHub path '${filePath}' is not a file.","messagePattern":"GitHub path '(.+?)' is not a file\\.","errorType":"exception","errorClass":"ToolError","httpStatus":null,"severity":"error","filePath":"packages/coding-agent/src/tools/gh.ts","lineNumber":294,"sourceCode":"\t\t\"api\",\n\t\t...ghApiHostArgs(ref),\n\t\t`/repos/${ref.slug}/contents/${endpointPath}`,\n\t\t\"--method\",\n\t\t\"GET\",\n\t\t\"-H\",\n\t\t\"Accept: application/vnd.github+json\",\n\t\t\"-H\",\n\t\t\"Accept-Encoding: identity\",\n\t];\n\tif (branch) {\n\t\targs.push(\"-f\", `ref=${branch}`);\n\t}\n\tconst response = await github.json<GitHubContentsResponse>(session.cwd, args, signal, {\n\t\trepoProvided: true,\n\t\ttrimOutput: false,\n\t});\n\tif (!isGitHubContentsFile(response)) {\n\t\tthrow new ToolError(`GitHub path '${filePath}' is not a file.`);\n\t}\n\n\t// A host-less ref went to gh's default host, so the link has to match it.\n\tconst fallbackHost = ref.host ?? defaultGhHost();\n\tconst fallbackSourceUrl = `https://${fallbackHost}/${ref.slug}/blob/${encodeURIComponent(branch ?? \"HEAD\")}/${endpointPath}`;\n\tconst sourceUrl = response.html_url || fallbackSourceUrl;\n\tif (response.encoding !== \"base64\" || typeof response.content !== \"string\") {\n\t\tconst size =\n\t\t\ttypeof response.size === \"number\" && response.size >= 0 ? formatBytes(response.size) : \"unknown size\";\n\t\treturn buildTextResult(\n\t\t\t`[GitHub did not return file bytes for '${filePath}' (${size}). Open ${sourceUrl} to view it.]`,\n\t\t\tsourceUrl,\n\t\t\t{ repo, branch },\n\t\t);\n\t}\n\n\tconst encoded = response.content.replaceAll(/\\s/g, \"\");\n\tconst bytes = Buffer.from(encoded, \"base64\");","sourceCodeStart":276,"sourceCodeEnd":312,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/coding-agent/src/tools/gh.ts#L276-L312","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"// before\nawait executeFileRead({ path: \"src\" });\n// after\nawait executeFileRead({ path: \"src/index.ts\" });","handlingStrategy":"type-guard","validationCode":"// Preflight: ensure the path is not obviously a directory by checking the tree listing first if available,\n// or require the path to contain an extension heuristically\nif (!path.includes(\".\")) console.warn(`'${path}' may be a directory, not a file`);","typeGuard":"function isFileContents(r: unknown): r is GitHubContentsFile {\n  return typeof r === \"object\" && r !== null && \"type\" in r && (r as { type: string }).type === \"file\";\n}","tryCatchPattern":"try {\n  const result = await gh.readFile({ path, ref });\n} catch (err) {\n  if (err instanceof ToolError && /is not a file/.test(err.message)) {\n    // fall back to listing the parent directory or fetching the dir listing\n  } else throw err;\n}","preventionTips":["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"],"tags":["github","path","type-mismatch"],"backgroundTag":"path-is-not-a-file","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}