chatboxai/chatbox · error · GitHubApiError
Failed to fetch file: ${filePath}
Error message
Failed to fetch file: ${filePath} What it means
Thrown by fetchFileContent() when raw.githubusercontent.com returns a non-OK status for a single file. Unlike githubFetch (which special-cases 404 and 403), this raw-URL path has no status-specific branches — any failure becomes a GitHubApiError with the original status code attached. The function powers SKILL.md reads during detection and individual file downloads.
Source
Thrown at src/main/skills/github-fetcher.ts:324
}
return detected
}
export async function fetchFileContent(owner: string, repo: string, filePath: string): Promise<string> {
// Git allows `#`/`?` in filenames — encode per segment so they don't truncate the URL
const encodedPath = filePath.split('/').map(encodeURIComponent).join('/')
const url = `https://raw.githubusercontent.com/${owner}/${repo}/HEAD/${encodedPath}`
const cached = getCached<string>(url)
if (cached !== undefined) return cached
const response = await fetch(url, {
headers: { 'User-Agent': USER_AGENT },
})
if (!response.ok) {
throw new GitHubApiError(`Failed to fetch file: ${filePath}`, response.status)
}
const content = await response.text()
setCache(url, content)
return content
}
export async function downloadSkillFiles(
owner: string,
repo: string,
skillPath: string,
targetDir: string
): Promise<void> {
try {
const downloaded = await downloadSkillFilesViaTree(owner, repo, skillPath, targetDir)
if (downloaded) return
} catch (error) {
if (error instanceof GitHubApiError && (error.statusCode === 403 || error.statusCode === 429)) {View on GitHub (pinned to 81571269ad)
Solutions
- Verify the file exists at the repo's default branch by opening the raw URL in a browser.
- If the repo is private, supply authentication — raw URLs need a token in the URL or use the contents API with an Authorization header.
- Retry once after a short delay for transient CDN 5xx; clearCache() is not needed since this URL was never cached on failure.
- For LFS files, fetch via the contents/blobs API instead of raw.
Example fix
// before
if (!response.ok) {
throw new GitHubApiError(`Failed to fetch file: ${filePath}`, response.status)
}
// after — surface the status so callers can distinguish 404 (gone) from 5xx (retry)
if (!response.ok) {
throw new GitHubApiError(
`Failed to fetch file: ${filePath} (HTTP ${response.status} ${response.statusText})`,
response.status
)
} Defensive patterns
Strategy: retry
Validate before calling
// Validate the file path shape before fetching.
function isValidFilePath(p: string): boolean {
return typeof p === 'string' && p.length > 0 && p.length < 4096 && !p.includes('\\')
}
if (!isValidFilePath(filePath)) throw new Error('Invalid file path') Type guard
function isGitHubApiError(e: unknown): e is { statusCode: number; message: string } {
return e instanceof Error && typeof (e as any).statusCode === 'number'
} Try / catch
try {
return await fetchFileContent(owner, repo, filePath)
} catch (error) {
if (isGitHubApiError(error) && (error.statusCode === 404)) return null // file gone
if (isGitHubApiError(error) && error.statusCode >= 500) {
return await backoffRetry(() => fetchFileContent(owner, repo, filePath))
}
throw error
} Prevention
- Expect 404 from raw URLs when HEAD has moved — do not cache failure, just return null.
- Use the contents/blobs API for private repos or LFS files instead of raw.githubusercontent.com.
- Encode each path segment with encodeURIComponent to handle # and ? in filenames.
When it happens
Trigger: Requesting raw.githubusercontent.com/{owner}/{repo}/HEAD/{encodedPath} where the path does not exist at HEAD (404), the repo is private (404), the file is git-LFS tracked (pointer served, or 404 on media), or raw CDN returns 5xx. Also triggered when filePath contains characters that encodeURIComponent mishandles for the raw endpoint.
Common situations: A SKILL.md listed in the tree was deleted before the content fetch (HEAD advanced); private repo accessed without a token; LFS-backed file; transient raw CDN error. The per-segment encodeURIComponent handles # and ? in filenames, but a leading slash or unusual encoding can still produce a 404.
Related errors
- GitHub API error: ${response.status} ${response.statusText}
- Found ${limitedPaths.length} skill(s) in ${owner}/${repo} bu
- Knowledge base name cannot be empty
- ${response.status} ${response.statusText}: ${text}
- Failed to start preview server
AI-assisted analysis of chatboxai/chatbox@81571269ad (2026-08-12).
Data as JSON: /api/errors/d947627dd7cbe2a4.
Report an issue: GitHub.