fix: add clear error message for non-ASCII characters in DeepSeek API key

- Validate API key for non-ASCII characters before passing to OpenAI client
- Provide user-friendly error message explaining the issue
- Add comprehensive test cases for API key validation

Fixes #7483
This commit is contained in:
Roo Code 2025-08-28 08:42:16 +00:00
parent 548d3b48a9
commit 3507143764
2 changed files with 79 additions and 1 deletions

View file

@ -96,6 +96,60 @@ describe("DeepSeekHandler", () => {
expect(handler.getModel().id).toBe(mockOptions.apiModelId)
})
it("should throw error if API key contains non-ASCII characters", () => {
expect(() => {
new DeepSeekHandler({
...mockOptions,
deepSeekApiKey: "sk-test中文characters",
})
}).toThrow("Invalid DeepSeek API key: contains non-ASCII character at position 8")
})
it("should throw error with helpful message for non-ASCII characters", () => {
expect(() => {
new DeepSeekHandler({
...mockOptions,
deepSeekApiKey: "sk-test-😀-key",
})
}).toThrow(/API keys must contain only ASCII characters/)
})
it("should accept valid ASCII-only API keys", () => {
expect(() => {
new DeepSeekHandler({
...mockOptions,
deepSeekApiKey: "sk-test-1234567890-abcdefghijklmnopqrstuvwxyz-ABCDEFGHIJKLMNOPQRSTUVWXYZ",
})
}).not.toThrow()
})
it("should accept API keys with standard symbols", () => {
expect(() => {
new DeepSeekHandler({
...mockOptions,
deepSeekApiKey: "sk-test_key-123!@#$%^&*()_+-=[]{}|;:,.<>?/",
})
}).not.toThrow()
})
it("should not validate when API key is not provided", () => {
expect(() => {
new DeepSeekHandler({
...mockOptions,
deepSeekApiKey: undefined,
})
}).not.toThrow()
})
it("should not validate when API key is 'not-provided'", () => {
expect(() => {
new DeepSeekHandler({
...mockOptions,
deepSeekApiKey: "not-provided",
})
}).not.toThrow()
})
it.skip("should throw error if API key is missing", () => {
expect(() => {
new DeepSeekHandler({

View file

@ -9,9 +9,13 @@ import { OpenAiHandler } from "./openai"
export class DeepSeekHandler extends OpenAiHandler {
constructor(options: ApiHandlerOptions) {
// Validate API key before passing to parent constructor
const apiKey = options.deepSeekApiKey ?? "not-provided"
DeepSeekHandler.validateApiKey(apiKey)
super({
...options,
openAiApiKey: options.deepSeekApiKey ?? "not-provided",
openAiApiKey: apiKey,
openAiModelId: options.apiModelId ?? deepSeekDefaultModelId,
openAiBaseUrl: options.deepSeekBaseUrl ?? "https://api.deepseek.com",
openAiStreamingEnabled: true,
@ -19,6 +23,26 @@ export class DeepSeekHandler extends OpenAiHandler {
})
}
/**
* Validates that the API key contains only ASCII characters.
* Non-ASCII characters in API keys cause ByteString conversion errors.
*/
private static validateApiKey(apiKey: string): void {
if (apiKey && apiKey !== "not-provided") {
// Check for non-ASCII characters
for (let i = 0; i < apiKey.length; i++) {
const charCode = apiKey.charCodeAt(i)
if (charCode > 255) {
throw new Error(
`Invalid DeepSeek API key: contains non-ASCII character at position ${i + 1}. ` +
`API keys must contain only ASCII characters (letters, numbers, and standard symbols). ` +
`Please check your API key for any accidental non-ASCII characters or spaces.`,
)
}
}
}
}
override getModel() {
const id = this.options.apiModelId ?? deepSeekDefaultModelId
const info = deepSeekModels[id as keyof typeof deepSeekModels] || deepSeekModels[deepSeekDefaultModelId]