From 2e8850edd58e969645dbc4bd53afe69aeb55e3cb Mon Sep 17 00:00:00 2001 From: daewoongoh Date: Fri, 3 Jul 2026 08:30:54 +0900 Subject: [PATCH] fix: avoid unsafe array index access in AnthropicVertex completePrompt The completePrompt method accessed response.content[0] directly, which could throw a TypeError when the content array is empty, and would return an empty string when the first block is a non-text block (e.g. thinking or tool_use) even when a text block is present later in the array. Use Array.prototype.find() to locate the first text block, matching the pattern already used in anthropic.ts. This safely returns undefined for empty arrays and surfaces the actual text response regardless of its position among content blocks. Add regression tests covering an empty content array and a mixed content array where a thinking block precedes the text block. Signed-off-by: daewoongoh --- .../__tests__/anthropic-vertex.spec.ts | 39 ++++++++++++++++++- src/api/providers/anthropic-vertex.ts | 8 +--- 2 files changed, 40 insertions(+), 7 deletions(-) diff --git a/src/api/providers/__tests__/anthropic-vertex.spec.ts b/src/api/providers/__tests__/anthropic-vertex.spec.ts index 6b56c5af98..d697757853 100644 --- a/src/api/providers/__tests__/anthropic-vertex.spec.ts +++ b/src/api/providers/__tests__/anthropic-vertex.spec.ts @@ -895,6 +895,41 @@ describe("VertexHandler", () => { const result = await handler.completePrompt("Test prompt") expect(result).toBe("") }) + + it("should handle empty content array for Claude", async () => { + handler = new AnthropicVertexHandler({ + apiModelId: "claude-3-5-sonnet-v2@20241022", + vertexProjectId: "test-project", + vertexRegion: "us-central1", + }) + + const mockCreate = vitest.fn().mockResolvedValue({ + content: [], + }) + ;(handler["client"].messages as any).create = mockCreate + + const result = await handler.completePrompt("Test prompt") + expect(result).toBe("") + }) + + it("should return text from first text block when mixed content for Claude", async () => { + handler = new AnthropicVertexHandler({ + apiModelId: "claude-3-5-sonnet-v2@20241022", + vertexProjectId: "test-project", + vertexRegion: "us-central1", + }) + + const mockCreate = vitest.fn().mockResolvedValue({ + content: [ + { type: "thinking", thinking: "internal reasoning" }, + { type: "text", text: "visible response" }, + ], + }) + ;(handler["client"].messages as any).create = mockCreate + + const result = await handler.completePrompt("Test prompt") + expect(result).toBe("visible response") + }) }) describe("getModel", () => { @@ -1342,7 +1377,9 @@ describe("VertexHandler", () => { })) ;(sonnetHandler["client"].messages as any).create = mockCreate - await sonnetHandler.createMessage("You are a helpful assistant", [{ role: "user", content: "Hello" }]).next() + await sonnetHandler + .createMessage("You are a helpful assistant", [{ role: "user", content: "Hello" }]) + .next() expect(mockCreate).toHaveBeenCalledWith( expect.objectContaining({ diff --git a/src/api/providers/anthropic-vertex.ts b/src/api/providers/anthropic-vertex.ts index 9089562f4f..4f62fe0b1e 100644 --- a/src/api/providers/anthropic-vertex.ts +++ b/src/api/providers/anthropic-vertex.ts @@ -297,13 +297,9 @@ export class AnthropicVertexHandler extends BaseProvider implements SingleComple } as Anthropic.Messages.MessageCreateParamsNonStreaming const response = await this.client.messages.create(params) - const content = response.content[0] + const content = response.content.find(({ type }) => type === "text") - if (content.type === "text") { - return content.text - } - - return "" + return content?.type === "text" ? content.text : "" } catch (error) { if (error instanceof Error) { throw new Error(`Vertex completion error: ${error.message}`)