Skip to content

Commit fb97be1

Browse files
committed
fix: handle empty files in get_file_contents
1. Empty (0-byte) files caused an unhandled error because the GitHub API returns null content with base64 encoding for them; GetContent() fails with "malformed response: base64 encoding of null content". Return empty text/plain content directly, bypassing decoding entirely.
1 parent 851030c commit fb97be1

File tree

2 files changed

+48
-0
lines changed

2 files changed

+48
-0
lines changed

pkg/github/repositories.go

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -731,6 +731,20 @@ func GetFileContents(t translations.TranslationHelperFunc) inventory.ServerTool
731731
successNote = fmt.Sprintf(" Note: the provided ref '%s' does not exist, default branch '%s' was used instead.", originalRef, rawOpts.Ref)
732732
}
733733

734+
// Empty files (0 bytes) have no content to decode; return
735+
// them directly as empty text to avoid errors from
736+
// GetContent when the API returns null content with a
737+
// base64 encoding field, and to avoid DetectContentType
738+
// misclassifying them as binary.
739+
if fileSize == 0 {
740+
result := &mcp.ResourceContents{
741+
URI: resourceURI,
742+
Text: "",
743+
MIMEType: "text/plain",
744+
}
745+
return utils.NewToolResultResource(fmt.Sprintf("successfully downloaded empty file (SHA: %s)%s", fileSHA, successNote), result), nil, nil
746+
}
747+
734748
// For files >= 1MB, return a ResourceLink instead of content
735749
const maxContentSize = 1024 * 1024 // 1MB
736750
if fileSize >= maxContentSize {

pkg/github/repositories_test.go

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -351,6 +351,40 @@ func Test_GetFileContents(t *testing.T) {
351351
Title: "File: large-file.bin",
352352
},
353353
},
354+
{
355+
name: "successful empty file content fetch",
356+
mockedClient: MockHTTPClientWithHandlers(map[string]http.HandlerFunc{
357+
GetReposGitRefByOwnerByRepoByRef: mockResponse(t, http.StatusOK, "{\"ref\": \"refs/heads/main\", \"object\": {\"sha\": \"\"}}"),
358+
GetReposByOwnerByRepo: mockResponse(t, http.StatusOK, "{\"name\": \"repo\", \"default_branch\": \"main\"}"),
359+
GetReposContentsByOwnerByRepoByPath: func(w http.ResponseWriter, _ *http.Request) {
360+
w.WriteHeader(http.StatusOK)
361+
fileContent := &github.RepositoryContent{
362+
Name: github.Ptr(".gitkeep"),
363+
Path: github.Ptr(".gitkeep"),
364+
SHA: github.Ptr("empty123"),
365+
Type: github.Ptr("file"),
366+
Content: nil,
367+
Size: github.Ptr(0),
368+
Encoding: github.Ptr("base64"),
369+
}
370+
contentBytes, _ := json.Marshal(fileContent)
371+
_, _ = w.Write(contentBytes)
372+
},
373+
}),
374+
requestArgs: map[string]any{
375+
"owner": "owner",
376+
"repo": "repo",
377+
"path": ".gitkeep",
378+
"ref": "refs/heads/main",
379+
},
380+
expectError: false,
381+
expectedResult: mcp.ResourceContents{
382+
URI: "repo://owner/repo/refs/heads/main/contents/.gitkeep",
383+
Text: "",
384+
MIMEType: "text/plain",
385+
},
386+
expectedMsg: "successfully downloaded empty file",
387+
},
354388
{
355389
name: "content fetch fails",
356390
mockedClient: MockHTTPClientWithHandlers(map[string]http.HandlerFunc{

0 commit comments

Comments
 (0)