Improve error handling for OpenAI API calls

Add comprehensive try-catch error handling around OpenAI API calls with
specific error messages for common failure scenarios:
- Invalid API key (401)
- Rate limiting (429)
- Model not found (404)
- Invalid request/content too long (400)
- Network errors (ENOTFOUND, ECONNREFUSED)
- Generic API errors with message passthrough

Remove redundant API key validation from OpenAIService - validation is
already handled in main.ts before service instantiation, eliminating
inconsistent error handling pattern (returning empty string vs throwing).

Update APIError type to include status and code fields, and mark as
deprecated since errors are now thrown as standard Error objects with
user-friendly messages.

Fixes H1 (Missing error handling) and H4 (Inconsistent API key check)
from CODE_REVIEW.md

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Mark Ayers 2025-10-12 19:44:23 -04:00
parent 635b1c6421
commit 1bdc85e085
2 changed files with 39 additions and 24 deletions

View file

@ -24,13 +24,6 @@ export class OpenAIService {
timestamp: string,
sourceFilePath: string,
): Promise<string> {
if (!this.apiKey) {
new Notice(
"OpenAI API key is not set. Please configure it in plugin settings.",
);
return "";
}
// Inform user about network request
new Notice("Sending request to OpenAI...", 3000);
@ -39,23 +32,42 @@ export class OpenAIService {
dangerouslyAllowBrowser: true,
});
const completion = await openai.chat.completions.create({
model: this.model,
messages: [
{
role: "system",
content: this.getSystemPrompt(sourceFilePath),
},
{
role: "user",
content: content,
},
],
temperature: 0.7,
max_tokens: 4000,
});
try {
const completion = await openai.chat.completions.create({
model: this.model,
messages: [
{
role: "system",
content: this.getSystemPrompt(sourceFilePath),
},
{
role: "user",
content: content,
},
],
temperature: 0.7,
max_tokens: 4000,
});
return completion.choices[0]?.message?.content ?? "";
return completion.choices[0]?.message?.content ?? "";
} catch (error: any) {
// Handle specific OpenAI API errors
if (error?.status === 401) {
throw new Error("Invalid OpenAI API key. Please check your settings.");
} else if (error?.status === 429) {
throw new Error("OpenAI API rate limit exceeded. Please try again later.");
} else if (error?.status === 404) {
throw new Error(`Model '${this.model}' not found. Please check your model selection.`);
} else if (error?.status === 400) {
throw new Error("Invalid request to OpenAI. The content may be too long or contain invalid characters.");
} else if (error?.code === "ENOTFOUND" || error?.code === "ECONNREFUSED") {
throw new Error("Network error. Please check your internet connection.");
} else if (error?.message) {
throw new Error(`OpenAI API error: ${error.message}`);
} else {
throw new Error("Failed to generate atomic notes. Please try again.");
}
}
}
/**

View file

@ -1,9 +1,12 @@
/**
* Error type for OpenAI API errors
* @deprecated No longer used - errors are now thrown as standard Error objects with descriptive messages
*/
export interface APIError extends Error {
response?: {
status: number;
data: any;
data: unknown;
};
status?: number;
code?: string;
}