Skip to content

Commit 388cc89

Browse files
claudeJimStenstrom
authored andcommitted
fix: improve error handling for Ollama JSON parsing errors (#87)
Enhances error handling and configurability for issue #87 where Ollama returns malformed JSON causing RetryError failures. Changes: - Add specific detection and helpful error messages for Ollama unmarshal errors - Provide actionable troubleshooting steps (restart Ollama, re-pull model, etc.) - Add configurable maxRetries option to AIProviderConfig - Pass maxRetries to AI SDK generateText and streamText calls - Default maxRetries to 2 (same as AI SDK default) This helps users understand that the issue is with their Ollama server, not nanocoder, and gives them clear steps to resolve it.
1 parent 3a2f87a commit 388cc89

File tree

2 files changed

+26
-0
lines changed

2 files changed

+26
-0
lines changed

source/ai-sdk-client.ts

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,24 @@ function parseAPIError(error: unknown): string {
2424

2525
const errorMessage = error.message;
2626

27+
// Handle Ollama-specific unmarshal/JSON parsing errors
28+
if (
29+
errorMessage.includes('unmarshal') ||
30+
(errorMessage.includes('invalid character') &&
31+
errorMessage.includes('after top-level value'))
32+
) {
33+
return (
34+
'Ollama server error: The model returned malformed JSON. ' +
35+
'This usually indicates an issue with the Ollama server or model. ' +
36+
'Try:\n' +
37+
' 1. Restart Ollama: systemctl restart ollama (Linux) or restart the Ollama app\n' +
38+
' 2. Re-pull the model: ollama pull <model-name>\n' +
39+
' 3. Check Ollama logs for more details\n' +
40+
' 4. Try a different model to see if the issue is model-specific\n' +
41+
`Original error: ${errorMessage}`
42+
);
43+
}
44+
2745
// Extract status code and clean message from common error patterns
2846
const statusMatch = errorMessage.match(
2947
/(?:Error: )?(\d{3})\s+(?:\d{3}\s+)?(?:Bad Request|[^:]+):\s*(.+)/i,
@@ -138,12 +156,15 @@ export class AISDKClient implements LLMClient {
138156
private providerConfig: AIProviderConfig;
139157
private undiciAgent: Agent;
140158
private cachedContextSize: number;
159+
private maxRetries: number;
141160

142161
constructor(providerConfig: AIProviderConfig) {
143162
this.providerConfig = providerConfig;
144163
this.availableModels = providerConfig.models;
145164
this.currentModel = providerConfig.models[0] || '';
146165
this.cachedContextSize = 0;
166+
// Default to 2 retries (same as AI SDK default), or use configured value
167+
this.maxRetries = providerConfig.maxRetries ?? 2;
147168

148169
const {requestTimeout, socketTimeout, connectionPool} = this.providerConfig;
149170
const resolvedSocketTimeout =
@@ -263,6 +284,7 @@ export class AISDKClient implements LLMClient {
263284
messages: modelMessages,
264285
tools: aiTools,
265286
abortSignal: signal,
287+
maxRetries: this.maxRetries,
266288
});
267289

268290
// Extract tool calls from result
@@ -384,6 +406,7 @@ export class AISDKClient implements LLMClient {
384406
messages: modelMessages,
385407
tools: aiTools,
386408
abortSignal: signal,
409+
maxRetries: this.maxRetries,
387410
});
388411

389412
// Stream tokens

source/types/config.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ export interface AIProviderConfig {
77
models: string[];
88
requestTimeout?: number;
99
socketTimeout?: number;
10+
maxRetries?: number; // Maximum number of retries for failed requests (default: 2)
1011
connectionPool?: {
1112
idleTimeout?: number;
1213
cumulativeMaxIdleTimeout?: number;
@@ -26,6 +27,7 @@ export interface ProviderConfig {
2627
models: string[];
2728
requestTimeout?: number;
2829
socketTimeout?: number;
30+
maxRetries?: number; // Maximum number of retries for failed requests (default: 2)
2931
organizationId?: string;
3032
timeout?: number;
3133
connectionPool?: {
@@ -44,6 +46,7 @@ export interface AppConfig {
4446
models: string[];
4547
requestTimeout?: number;
4648
socketTimeout?: number;
49+
maxRetries?: number; // Maximum number of retries for failed requests (default: 2)
4750
connectionPool?: {
4851
idleTimeout?: number;
4952
cumulativeMaxIdleTimeout?: number;

0 commit comments

Comments
 (0)