Skip to content

Commit f608e94

Browse files
Merge pull request #45 from developerfred/improve-error-handling-code-runner
Improve Error Handling in Code Runner
2 parents 6ecef36 + 0d3cdbd commit f608e94

File tree

1 file changed

+120
-38
lines changed

1 file changed

+120
-38
lines changed

src/lib/hooks/useCodeRunner.ts

Lines changed: 120 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,19 @@ interface UseCodeRunnerOptions {
88
onRunStart?: () => void;
99
onRunComplete?: () => void;
1010
onRunError?: (error: Error) => void;
11+
timeoutMs?: number;
12+
maxRetries?: number;
13+
}
14+
15+
class CodeExecutionError extends Error {
16+
constructor(
17+
message: string,
18+
public readonly type: 'network' | 'timeout' | 'simulation' | 'unknown',
19+
public readonly originalError?: Error
20+
) {
21+
super(message);
22+
this.name = 'CodeExecutionError';
23+
}
1124
}
1225

1326
export function useCodeRunner(options: UseCodeRunnerOptions = {}) {
@@ -21,6 +34,11 @@ export function useCodeRunner(options: UseCodeRunnerOptions = {}) {
2134
timestamp: number;
2235
} | null>(null);
2336

37+
const {
38+
timeoutMs = 30000, // 30 seconds default
39+
maxRetries = 2,
40+
} = options;
41+
2442
const updateCode = useCallback((newCode: string) => {
2543
setCode(newCode);
2644
}, []);
@@ -40,52 +58,116 @@ export function useCodeRunner(options: UseCodeRunnerOptions = {}) {
4058
setOutputs([
4159
{
4260
type: "log",
43-
content: "Running code...",
61+
content: `🚀 Starting execution on ${network.name}...`,
4462
timestamp: Date.now(),
4563
},
4664
]);
4765

48-
try {
49-
setProgress(30);
50-
await new Promise((resolve) => setTimeout(resolve, 300));
51-
setProgress(50);
52-
53-
const simulatedOutputs = await simulateCodeExecution(example, network);
54-
setProgress(90);
55-
56-
await new Promise((resolve) => setTimeout(resolve, 200));
57-
58-
setOutputs(simulatedOutputs);
59-
setLastRun({
60-
example,
61-
network,
62-
timestamp: Date.now(),
63-
});
64-
65-
options.onRunComplete?.();
66-
} catch (error) {
67-
setOutputs([
68-
{
69-
type: "error",
70-
content:
71-
error instanceof Error
72-
? `Error: ${error.message}`
73-
: "An unknown error occurred",
66+
let attempt = 0;
67+
let lastError: Error | null = null;
68+
69+
while (attempt <= maxRetries) {
70+
try {
71+
setProgress(20 + (attempt * 10));
72+
73+
if (attempt > 0) {
74+
setOutputs(prev => [...prev, {
75+
type: "warning",
76+
content: `🔄 Retry attempt ${attempt}/${maxRetries}...`,
77+
timestamp: Date.now(),
78+
}]);
79+
}
80+
81+
// Create a timeout promise
82+
const timeoutPromise = new Promise<never>((_, reject) => {
83+
setTimeout(() => {
84+
reject(new CodeExecutionError(
85+
`Execution timed out after ${timeoutMs}ms`,
86+
'timeout'
87+
));
88+
}, timeoutMs);
89+
});
90+
91+
// Race between simulation and timeout
92+
const simulatedOutputs = await Promise.race([
93+
simulateCodeExecution(example, network),
94+
timeoutPromise
95+
]);
96+
97+
setProgress(90);
98+
99+
// Add success message
100+
const successOutputs = [
101+
...simulatedOutputs,
102+
{
103+
type: "log" as const,
104+
content: `✅ Execution completed successfully on ${network.name}`,
105+
timestamp: Date.now(),
106+
}
107+
];
108+
109+
setOutputs(successOutputs);
110+
setLastRun({
111+
example,
112+
network,
74113
timestamp: Date.now(),
75-
},
76-
]);
114+
});
115+
116+
options.onRunComplete?.();
117+
return; // Success, exit retry loop
118+
119+
} catch (error) {
120+
lastError = error instanceof Error ? error : new Error(String(error));
121+
attempt++;
122+
123+
let errorType: CodeExecutionError['type'] = 'unknown';
124+
let errorMessage = lastError.message;
125+
126+
// Categorize errors
127+
if (lastError instanceof CodeExecutionError) {
128+
errorType = lastError.type;
129+
} else if (lastError.message.includes('network') || lastError.message.includes('connection')) {
130+
errorType = 'network';
131+
errorMessage = `Network error: ${lastError.message}. Please check your internet connection and try again.`;
132+
} else if (lastError.message.includes('timeout')) {
133+
errorType = 'timeout';
134+
errorMessage = `Execution timed out: ${lastError.message}. The operation took too long to complete.`;
135+
} else {
136+
errorType = 'simulation';
137+
errorMessage = `Simulation error: ${lastError.message}`;
138+
}
139+
140+
const categorizedError = new CodeExecutionError(errorMessage, errorType, lastError);
141+
142+
// If this was the last attempt, report the error
143+
if (attempt > maxRetries) {
144+
setOutputs(prev => [...prev, {
145+
type: "error",
146+
content: `❌ ${categorizedError.message}${errorType === 'network' ? ' (Check network connection)' : ''}${errorType === 'timeout' ? ' (Try simplifying the code)' : ''}`,
147+
timestamp: Date.now(),
148+
}]);
149+
150+
options.onRunError?.(categorizedError);
151+
}
152+
153+
// Wait before retry (exponential backoff)
154+
if (attempt <= maxRetries) {
155+
const delay = Math.min(1000 * Math.pow(2, attempt - 1), 5000);
156+
await new Promise(resolve => setTimeout(resolve, delay));
157+
}
158+
}
159+
}
77160

78-
options.onRunError?.(
79-
error instanceof Error ? error : new Error("Unknown error"),
80-
);
81-
} finally {
82-
setIsRunning(false);
83-
setProgress(100);
161+
// If we get here, all retries failed
162+
setOutputs(prev => [...prev, {
163+
type: "error",
164+
content: `❌ All ${maxRetries + 1} attempts failed. Last error: ${lastError?.message || 'Unknown error'}`,
165+
timestamp: Date.now(),
166+
}]);
84167

85-
setTimeout(() => setProgress(0), 500);
86-
}
168+
options.onRunError?.(lastError || new Error('All retry attempts failed'));
87169
},
88-
[options],
170+
[options, timeoutMs, maxRetries],
89171
);
90172

91173
const clearOutput = useCallback(() => {

0 commit comments

Comments
 (0)