From 16529b7b932bc13db2962e0f8a3f81a9736788d1 Mon Sep 17 00:00:00 2001 From: daniel-lxs Date: Mon, 12 Jan 2026 18:04:07 -0500 Subject: [PATCH] feat(telemetry): add diagnostic properties to Gemini API error telemetry MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When Gemini API errors occur, the error messages are often too vague to diagnose the root cause. This change adds diagnostic properties to the captureException() call in the Gemini handler's createMessage() method. Properties added: - messageCount: Number of input messages - geminiContentsCount: Number of converted Gemini contents - hasToolUseBlocks: Whether messages contain tool_use blocks - hasToolResultBlocks: Whether messages contain tool_result blocks - hasImageBlocks: Whether messages contain image blocks - emptyPartsCount: Count of contents with empty parts (likely cause of INVALID_ARGUMENT) - toolIdToNameSize: Size of tool ID→name mapping - hasThinkingConfig: Whether thinking configuration is enabled - usingNativeTools: Whether native tools are in use - includeThoughtSignatures: Whether thought signatures are included Resolves ROO-515 --- src/api/providers/__tests__/gemini.spec.ts | 13 +++++++++ src/api/providers/gemini.ts | 31 +++++++++++++++++++++- 2 files changed, 43 insertions(+), 1 deletion(-) diff --git a/src/api/providers/__tests__/gemini.spec.ts b/src/api/providers/__tests__/gemini.spec.ts index 8c2ee87a787..84cfe42f376 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 1cc9228256c..06819921010 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 }))