Skip to content

Conversation

@bharatvansh
Copy link
Contributor

Description

Fixes an issue where the Gemini API rejects FunctionResponse payloads when the tool response is a JSON Array. The API requires the response field to be a JSON Object (Protobuf Struct).

When a tool returns a list (e.g. from GitHub MCP listing repos), the API throws the following error:

"Invalid JSON payload received... Proto field is not repeating, cannot start list."

Changes

  • Modified src/extension/byok/common/geminiMessageConverter.ts to detect if responsePayload is an array and wrap it in an object: { result: [...] }.
  • Added regression test in src/extension/byok/common/test/geminiMessageConverter.spec.ts.

Context / References

  • Root Cause: The Gemini FunctionResponse expects the response field to be a google.protobuf.Struct (JSON Object), not a ListValue. Sending a top-level array causes a protocol buffer type mismatch.
  • Similar reports:

Verification

  • Added new unit test should wrap array responses in an object which passes.
  • Ran existing tests with npx vitest run src/extension/byok/common/test/geminiMessageConverter.spec.ts.

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR fixes a bug where the Gemini API rejects FunctionResponse payloads when tool responses are JSON arrays. The fix wraps array responses in an object to satisfy the Gemini API's requirement that the response field be a JSON Object (Protobuf Struct) rather than a ListValue.

Key changes:

  • Modified the type check in geminiMessageConverter.ts to detect arrays and wrap them in { result: [...] } objects
  • Added a regression test to verify array responses are properly wrapped

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.

File Description
src/extension/byok/common/geminiMessageConverter.ts Added Array.isArray() check to wrap array responses in an object before sending to Gemini API
src/extension/byok/common/test/geminiMessageConverter.spec.ts Added test case verifying that array tool responses are wrapped in { result: [...] } format

Comment on lines +105 to +121
it('should wrap array responses in an object', () => {
const toolResult = new LanguageModelToolResultPart('listRepos_12345', [new LanguageModelTextPart('["repo1", "repo2"]')]);
const messages: LanguageModelChatMessage[] = [
{
role: LanguageModelChatMessageRole.Assistant,
content: [toolResult],
name: undefined
}
];

const result = apiMessageToGeminiMessage(messages);

expect(result.contents).toHaveLength(1);
expect(result.contents[0].role).toBe('user');
const fr: any = result.contents[0].parts![0];
expect(fr.functionResponse.response).toEqual({ result: ['repo1', 'repo2'] });
});
Copy link

Copilot AI Jan 8, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

While the test for array wrapping is good, the fix also wraps other primitive types (strings, numbers, booleans, null) in an object. Consider adding tests for these cases as well to ensure comprehensive coverage. For example:

  • Test with a string primitive: "simple string"
  • Test with a number: 42
  • Test with a boolean: true
  • Test with null: null

This would ensure that all code paths through the wrapping logic are tested.

Copilot uses AI. Check for mistakes.
if (textContent) {
// Handle case with text content (may also have images)
try {
responsePayload = JSON.parse(textContent);
Copy link

Copilot AI Jan 8, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider adding a comment explaining why arrays (and other primitive types) need to be wrapped in an object. This would help future maintainers understand the Gemini API requirement. For example:

// Wrap non-object values (arrays, primitives, null) in an object because
// Gemini's FunctionResponse expects response to be a google.protobuf.Struct (JSON Object)

This provides context about the API constraint that necessitates this wrapping behavior.

Suggested change
responsePayload = JSON.parse(textContent);
responsePayload = JSON.parse(textContent);
// Wrap non-object values (arrays, primitives, null) in an object because
// Gemini's FunctionResponse.response must be a google.protobuf.Struct (JSON object)

Copilot uses AI. Check for mistakes.
@aeschli aeschli assigned lramos15 and unassigned aeschli Jan 8, 2026
@lramos15 lramos15 assigned vijayupadya and unassigned lramos15 Jan 8, 2026
@bharatvansh bharatvansh force-pushed the fix/gemini-array-response branch from 4321f6c to cca7bbb Compare January 10, 2026 07:37
@bharatvansh
Copy link
Contributor Author

@vijayupadya Hi, can you please review this?

@vs-code-engineering vs-code-engineering bot added this to the January 2026 milestone Jan 12, 2026
@vijayupadya vijayupadya added the bug Issue identified by VS Code Team member as probable bug label Jan 12, 2026
@vijayupadya vijayupadya enabled auto-merge January 12, 2026 13:34
@vijayupadya vijayupadya added this pull request to the merge queue Jan 12, 2026
Merged via the queue into microsoft:main with commit bceaea3 Jan 12, 2026
9 checks passed
@bharatvansh bharatvansh deleted the fix/gemini-array-response branch January 12, 2026 14:11
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Issue identified by VS Code Team member as probable bug

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants