diff --git a/src/api/providers/__tests__/gemini.spec.ts b/src/api/providers/__tests__/gemini.spec.ts index 8c2ee87a78..84cfe42f37 100644 --- a/src/api/providers/__tests__/gemini.spec.ts +++ b/src/api/providers/__tests__/gemini.spec.ts @@ -274,6 +274,19 @@ describe("GeminiHandler", () => { modelId: GEMINI_MODEL_NAME, operation: "createMessage", }), + // Verify diagnostic properties are included + expect.objectContaining({ + messageCount: expect.any(Number), + geminiContentsCount: expect.any(Number), + hasToolUseBlocks: expect.any(Boolean), + hasToolResultBlocks: expect.any(Boolean), + hasImageBlocks: expect.any(Boolean), + emptyPartsCount: expect.any(Number), + toolIdToNameSize: expect.any(Number), + hasThinkingConfig: expect.any(Boolean), + usingNativeTools: expect.any(Boolean), + includeThoughtSignatures: expect.any(Boolean), + }), ) // Verify it's an ApiProviderError diff --git a/src/api/providers/gemini.ts b/src/api/providers/gemini.ts index 1cc9228256..0681992101 100644 --- a/src/api/providers/gemini.ts +++ b/src/api/providers/gemini.ts @@ -335,7 +335,36 @@ export class GeminiHandler extends BaseProvider implements SingleCompletionHandl } catch (error) { const errorMessage = error instanceof Error ? error.message : String(error) const apiError = new ApiProviderError(errorMessage, this.providerName, model, "createMessage") - TelemetryService.instance.captureException(apiError) + + // Add request diagnostics to help debug INVALID_ARGUMENT errors + TelemetryService.instance.captureException(apiError, { + // Message structure diagnostics + messageCount: messages.length, + geminiContentsCount: contents.length, + + // Content type diagnostics + hasToolUseBlocks: messages.some( + (m) => Array.isArray(m.content) && m.content.some((b: { type: string }) => b.type === "tool_use"), + ), + hasToolResultBlocks: messages.some( + (m) => + Array.isArray(m.content) && m.content.some((b: { type: string }) => b.type === "tool_result"), + ), + hasImageBlocks: messages.some( + (m) => Array.isArray(m.content) && m.content.some((b: { type: string }) => b.type === "image"), + ), + + // Conversion diagnostics - check for empty parts which cause INVALID_ARGUMENT + emptyPartsCount: contents.filter((c) => !c.parts || c.parts.length === 0).length, + + // Tool mapping diagnostics - incomplete mapping causes conversion errors + toolIdToNameSize: toolIdToName.size, + + // Configuration that might affect the request + hasThinkingConfig: Boolean(thinkingConfig), + usingNativeTools, + includeThoughtSignatures, + }) if (error instanceof Error) { throw new Error(t("common:errors.gemini.generate_stream", { error: error.message }))