fix: handle ByteString conversion errors in OpenAI embedders (#8008)

This commit is contained in:
Daniel 2025-09-15 21:56:20 -05:00 committed by GitHub
parent beb0a59470
commit 759454b455
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 54 additions and 5 deletions

View file

@ -30,11 +30,26 @@ vitest.mock("../../../../i18n", () => ({
"embeddings:textExceedsTokenLimit": `Text at index ${params?.index} exceeds maximum token limit (${params?.itemTokens} > ${params?.maxTokens}). Skipping.`,
"embeddings:rateLimitRetry": `Rate limit hit, retrying in ${params?.delayMs}ms (attempt ${params?.attempt}/${params?.maxRetries})`,
"embeddings:unknownError": "Unknown error",
"common:errors.api.invalidKeyInvalidChars":
"API key contains invalid characters. Please check your API key for special characters.",
}
return translations[key] || key
},
}))
// Mock i18n/setup module used by the error handler
vitest.mock("../../../../i18n/setup", () => ({
default: {
t: (key: string) => {
const translations: Record<string, string> = {
"common:errors.api.invalidKeyInvalidChars":
"API key contains invalid characters. Please check your API key for special characters.",
}
return translations[key] || key
},
},
}))
const MockedOpenAI = OpenAI as MockedClass<typeof OpenAI>
describe("OpenAICompatibleEmbedder", () => {
@ -114,6 +129,22 @@ describe("OpenAICompatibleEmbedder", () => {
"embeddings:validation.baseUrlRequired",
)
})
it("should handle API key with invalid characters (ByteString conversion error)", () => {
// API key with special characters that cause ByteString conversion error
const invalidApiKey = "sk-test•invalid" // Contains bullet character (U+2022)
// Mock the OpenAI constructor to throw ByteString error
MockedOpenAI.mockImplementationOnce(() => {
throw new Error(
"Cannot convert argument to a ByteString because the character at index 7 has a value of 8226 which is greater than 255.",
)
})
expect(() => new OpenAICompatibleEmbedder(testBaseUrl, invalidApiKey, testModelId)).toThrow(
"API key contains invalid characters",
)
})
})
describe("embedderInfo", () => {

View file

@ -12,6 +12,7 @@ import { withValidationErrorHandling, HttpError, formatEmbeddingError } from "..
import { TelemetryEventName } from "@roo-code/types"
import { TelemetryService } from "@roo-code/telemetry"
import { Mutex } from "async-mutex"
import { handleOpenAIError } from "../../../api/providers/utils/openai-error-handler"
interface EmbeddingItem {
embedding: string | number[]
@ -66,10 +67,18 @@ export class OpenAICompatibleEmbedder implements IEmbedder {
this.baseUrl = baseUrl
this.apiKey = apiKey
this.embeddingsClient = new OpenAI({
baseURL: baseUrl,
apiKey: apiKey,
})
// Wrap OpenAI client creation to handle invalid API key characters
try {
this.embeddingsClient = new OpenAI({
baseURL: baseUrl,
apiKey: apiKey,
})
} catch (error) {
// Use the error handler to transform ByteString conversion errors
throw handleOpenAIError(error, "OpenAI Compatible")
}
this.defaultModelId = modelId || getDefaultModelId("openai-compatible")
// Cache the URL type check for performance
this.isFullUrl = this.isFullEndpointUrl(baseUrl)

View file

@ -13,6 +13,7 @@ import { t } from "../../../i18n"
import { withValidationErrorHandling, formatEmbeddingError, HttpError } from "../shared/validation-helpers"
import { TelemetryEventName } from "@roo-code/types"
import { TelemetryService } from "@roo-code/telemetry"
import { handleOpenAIError } from "../../../api/providers/utils/openai-error-handler"
/**
* OpenAI implementation of the embedder interface with batching and rate limiting
@ -28,7 +29,15 @@ export class OpenAiEmbedder extends OpenAiNativeHandler implements IEmbedder {
constructor(options: ApiHandlerOptions & { openAiEmbeddingModelId?: string }) {
super(options)
const apiKey = this.options.openAiNativeApiKey ?? "not-provided"
this.embeddingsClient = new OpenAI({ apiKey })
// Wrap OpenAI client creation to handle invalid API key characters
try {
this.embeddingsClient = new OpenAI({ apiKey })
} catch (error) {
// Use the error handler to transform ByteString conversion errors
throw handleOpenAIError(error, "OpenAI")
}
this.defaultModelId = options.openAiEmbeddingModelId || "text-embedding-3-small"
}