Skip to content

Commit 07fef73

Browse files
author
Gerome El-assaad
committed
Fix messaging errors and improve error handling
- Fix template parameter error in chat API route that was causing 'Cannot read properties of undefined' errors - Add comprehensive error handling with structured responses across API routes - Improve user-facing error messages with specific, actionable feedback - Add retry logic for network errors in chat submission - Enhance E2B sandbox error handling with proper cleanup - Add null safety checks to prevent undefined property access - Replace generic 'error please try again' with detailed error types and messages
1 parent 16f2d2e commit 07fef73

File tree

7 files changed

+290
-216
lines changed

7 files changed

+290
-216
lines changed

app/api/chat/route.ts

Lines changed: 62 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -76,46 +76,100 @@ export async function POST(req: Request) {
7676

7777
return stream.toTextStreamResponse()
7878
} catch (error: any) {
79+
console.error('Chat API Error:', {
80+
message: error?.message,
81+
status: error?.statusCode,
82+
provider: model,
83+
stack: error?.stack
84+
})
85+
7986
const isRateLimitError =
80-
error && (error.statusCode === 429 || error.message.includes('limit'))
87+
error && (error.statusCode === 429 || error.message.includes('limit') || error.message.includes('rate'))
8188
const isOverloadedError =
8289
error && (error.statusCode === 529 || error.statusCode === 503)
8390
const isAccessDeniedError =
84-
error && (error.statusCode === 403 || error.statusCode === 401)
91+
error && (error.statusCode === 403 || error.statusCode === 401 || error.message.includes('unauthorized') || error.message.includes('invalid') && error.message.includes('key'))
92+
const isModelError =
93+
error && (error.statusCode === 404 || error.message.includes('not found') || error.message.includes('model'))
94+
const isNetworkError =
95+
error && (error.code === 'ECONNREFUSED' || error.code === 'ETIMEDOUT' || error.message.includes('network'))
8596

8697
if (isRateLimitError) {
8798
return new Response(
88-
'The provider is currently unavailable due to request limit. Try using your own API key.',
99+
JSON.stringify({
100+
error: 'Rate limit exceeded. Please try again later or use your own API key.',
101+
type: 'rate_limit',
102+
retryAfter: 60
103+
}),
89104
{
90105
status: 429,
106+
headers: { 'Content-Type': 'application/json' }
91107
},
92108
)
93109
}
94110

95111
if (isOverloadedError) {
96112
return new Response(
97-
'The provider is currently unavailable. Please try again later.',
113+
JSON.stringify({
114+
error: 'The AI service is currently overloaded. Please try again in a few moments.',
115+
type: 'service_overload',
116+
retryAfter: 30
117+
}),
98118
{
99-
status: 529,
119+
status: 503,
120+
headers: { 'Content-Type': 'application/json' }
100121
},
101122
)
102123
}
103124

104125
if (isAccessDeniedError) {
105126
return new Response(
106-
'Access denied. Please make sure your API key is valid.',
127+
JSON.stringify({
128+
error: 'Invalid API key or access denied. Please check your API key configuration.',
129+
type: 'auth_error'
130+
}),
107131
{
108132
status: 403,
133+
headers: { 'Content-Type': 'application/json' }
109134
},
110135
)
111136
}
112137

113-
console.error('Error:', error)
138+
if (isModelError) {
139+
return new Response(
140+
JSON.stringify({
141+
error: 'The selected AI model is not available. Please try a different model.',
142+
type: 'model_error'
143+
}),
144+
{
145+
status: 400,
146+
headers: { 'Content-Type': 'application/json' }
147+
},
148+
)
149+
}
150+
151+
if (isNetworkError) {
152+
return new Response(
153+
JSON.stringify({
154+
error: 'Network connection failed. Please check your internet connection and try again.',
155+
type: 'network_error'
156+
}),
157+
{
158+
status: 502,
159+
headers: { 'Content-Type': 'application/json' }
160+
},
161+
)
162+
}
114163

115164
return new Response(
116-
'An unexpected error has occurred. Please try again later.',
165+
JSON.stringify({
166+
error: 'An unexpected error occurred. Please try again. If the problem persists, try using a different AI model.',
167+
type: 'unknown_error',
168+
details: error?.message || 'Unknown error'
169+
}),
117170
{
118171
status: 500,
172+
headers: { 'Content-Type': 'application/json' }
119173
},
120174
)
121175
}

app/api/sandbox/route.ts

Lines changed: 127 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -7,73 +7,140 @@ const sandboxTimeout = 10 * 60 * 1000
77
export const maxDuration = 60
88

99
export async function POST(req: Request) {
10-
const {
11-
fragment,
12-
userID,
13-
teamID,
14-
accessToken,
15-
}: {
16-
fragment: FragmentSchema
17-
userID: string | undefined
18-
teamID: string | undefined
19-
accessToken: string | undefined
20-
} = await req.json()
10+
try {
11+
const {
12+
fragment,
13+
userID,
14+
teamID,
15+
accessToken,
16+
}: {
17+
fragment: FragmentSchema
18+
userID: string | undefined
19+
teamID: string | undefined
20+
accessToken: string | undefined
21+
} = await req.json()
2122

22-
const sbx = await Sandbox.create(fragment.template, {
23-
metadata: {
24-
template: fragment.template,
25-
userID: userID ?? '',
26-
teamID: teamID ?? '',
27-
},
28-
timeoutMs: sandboxTimeout,
29-
...(teamID && accessToken
30-
? {
31-
headers: {
32-
'X-Supabase-Team': teamID,
33-
'X-Supabase-Token': accessToken,
34-
},
35-
}
36-
: {}),
37-
})
23+
if (!fragment) {
24+
return new Response(
25+
JSON.stringify({
26+
error: 'Missing fragment data',
27+
type: 'validation_error'
28+
}),
29+
{ status: 400, headers: { 'Content-Type': 'application/json' } }
30+
)
31+
}
3832

39-
if (fragment.has_additional_dependencies) {
40-
await sbx.commands.run(fragment.install_dependencies_command)
41-
}
33+
if (!process.env.E2B_API_KEY) {
34+
console.error('E2B_API_KEY environment variable not found')
35+
return new Response(
36+
JSON.stringify({
37+
error: 'Code execution service is not configured. Please check environment settings.',
38+
type: 'config_error'
39+
}),
40+
{ status: 503, headers: { 'Content-Type': 'application/json' } }
41+
)
42+
}
4243

43-
if (fragment.code && Array.isArray(fragment.code)) {
44-
fragment.code.forEach(async (file) => {
45-
await sbx.files.write(file.file_path, file.file_content)
46-
})
47-
} else {
48-
await sbx.files.write(fragment.file_path, fragment.code)
49-
}
44+
let sbx
45+
try {
46+
sbx = await Sandbox.create(fragment.template, {
47+
metadata: {
48+
template: fragment.template,
49+
userID: userID ?? '',
50+
teamID: teamID ?? '',
51+
},
52+
timeoutMs: sandboxTimeout,
53+
...(teamID && accessToken
54+
? {
55+
headers: {
56+
'X-Supabase-Team': teamID,
57+
'X-Supabase-Token': accessToken,
58+
},
59+
}
60+
: {}),
61+
})
62+
} catch (e2bError: any) {
63+
console.error('E2B Sandbox creation failed:', e2bError)
64+
return new Response(
65+
JSON.stringify({
66+
error: 'Failed to create sandbox environment. Please try again later.',
67+
type: 'sandbox_creation_error',
68+
details: e2bError.message
69+
}),
70+
{ status: 503, headers: { 'Content-Type': 'application/json' } }
71+
)
72+
}
73+
74+
try {
75+
if (fragment.has_additional_dependencies) {
76+
await sbx.commands.run(fragment.install_dependencies_command)
77+
}
78+
79+
if (fragment.code && Array.isArray(fragment.code)) {
80+
await Promise.all(fragment.code.map(async (file) => {
81+
await sbx.files.write(file.file_path, file.file_content)
82+
}))
83+
} else {
84+
await sbx.files.write(fragment.file_path, fragment.code)
85+
}
86+
87+
if (fragment.template === 'code-interpreter-v1') {
88+
const { logs, error, results } = await sbx.runCode(fragment.code || '')
5089

51-
if (fragment.template === 'code-interpreter-v1') {
52-
const { logs, error, results } = await sbx.runCode(fragment.code || '')
90+
return new Response(
91+
JSON.stringify({
92+
sbxId: sbx?.sandboxId,
93+
template: fragment.template,
94+
stdout: logs.stdout,
95+
stderr: logs.stderr,
96+
runtimeError: error,
97+
cellResults: results,
98+
} as ExecutionResultInterpreter),
99+
{ headers: { 'Content-Type': 'application/json' } }
100+
)
101+
}
53102

103+
await sbx.commands.run(fragment.install_dependencies_command, {
104+
envs: {
105+
PORT: (fragment.port || 80).toString(),
106+
},
107+
})
108+
109+
return new Response(
110+
JSON.stringify({
111+
sbxId: sbx?.sandboxId,
112+
template: fragment.template,
113+
url: `https://${sbx?.getHost(fragment.port || 80)}`,
114+
} as ExecutionResultWeb),
115+
{ headers: { 'Content-Type': 'application/json' } }
116+
)
117+
} catch (executionError: any) {
118+
console.error('Sandbox execution error:', executionError)
119+
120+
// Clean up sandbox on execution error
121+
try {
122+
await sbx?.kill()
123+
} catch {}
124+
125+
return new Response(
126+
JSON.stringify({
127+
error: 'Code execution failed. There may be an error in your code or dependencies.',
128+
type: 'execution_error',
129+
details: executionError.message
130+
}),
131+
{ status: 500, headers: { 'Content-Type': 'application/json' } }
132+
)
133+
}
134+
135+
} catch (error: any) {
136+
console.error('Sandbox API Error:', error)
54137
return new Response(
55138
JSON.stringify({
56-
sbxId: sbx?.sandboxId,
57-
template: fragment.template,
58-
stdout: logs.stdout,
59-
stderr: logs.stderr,
60-
runtimeError: error,
61-
cellResults: results,
62-
} as ExecutionResultInterpreter),
139+
error: 'An unexpected error occurred while setting up the sandbox.',
140+
type: 'unknown_error',
141+
details: error?.message || 'Unknown error'
142+
}),
143+
{ status: 500, headers: { 'Content-Type': 'application/json' } }
63144
)
64145
}
65-
66-
await sbx.commands.run(fragment.install_dependencies_command, {
67-
envs: {
68-
PORT: (fragment.port || 80).toString(),
69-
},
70-
})
71-
72-
return new Response(
73-
JSON.stringify({
74-
sbxId: sbx?.sandboxId,
75-
template: fragment.template,
76-
url: `https://${sbx?.getHost(fragment.port || 80)}`,
77-
} as ExecutionResultWeb),
78-
)
79146
}

app/page.tsx

Lines changed: 38 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -59,10 +59,6 @@ export default function Home() {
5959

6060
const { session, userTeam } = useAuth(setAuthDialog, setAuthView)
6161

62-
const currentTemplate =
63-
selectedTemplate === 'auto'
64-
? templates
65-
: templates[selectedTemplate as keyof typeof templates]
6662

6763
const { executeCode: enhancedExecuteCode } = useEnhancedChat({
6864
userID: session?.user?.id,
@@ -101,10 +97,35 @@ export default function Home() {
10197
schema,
10298
onError: (error) => {
10399
console.error('Error submitting request:', error)
104-
if (error.message.includes('limit')) {
105-
setIsRateLimited(true)
100+
101+
let displayMessage = error.message
102+
let isRateLimit = false
103+
104+
// Try to parse structured error response
105+
try {
106+
if (error.message.startsWith('{')) {
107+
const errorData = JSON.parse(error.message)
108+
displayMessage = errorData.error || error.message
109+
isRateLimit = errorData.type === 'rate_limit'
110+
} else {
111+
// Handle common error patterns
112+
if (error.message.includes('limit') || error.message.includes('rate')) {
113+
isRateLimit = true
114+
displayMessage = 'Rate limit exceeded. Please try again later or use your own API key.'
115+
} else if (error.message.includes('API key') || error.message.includes('unauthorized')) {
116+
displayMessage = 'Invalid API key. Please check your API key configuration in settings.'
117+
} else if (error.message.includes('network') || error.message.includes('fetch')) {
118+
displayMessage = 'Network error. Please check your connection and try again.'
119+
} else if (error.message.includes('timeout')) {
120+
displayMessage = 'Request timeout. Please try again.'
121+
}
122+
}
123+
} catch {
124+
// Use original error message if parsing fails
106125
}
107-
setErrorMessage(error.message)
126+
127+
setIsRateLimited(isRateLimit)
128+
setErrorMessage(displayMessage)
108129
},
109130
onFinish: async ({ object: fragment, error }) => {
110131
if (!error) {
@@ -124,6 +145,14 @@ export default function Home() {
124145
})
125146

126147
const result = await response.json()
148+
149+
if (!response.ok) {
150+
console.error('Sandbox creation failed:', result)
151+
setErrorMessage(result.error || 'Failed to create sandbox environment')
152+
setIsPreviewLoading(false)
153+
return
154+
}
155+
127156
posthog.capture('sandbox_created', { url: result.url })
128157

129158
setResult(result)
@@ -249,7 +278,7 @@ export default function Home() {
249278
userID: session?.user?.id,
250279
teamID: userTeam?.id,
251280
messages: toAISDKMessages(updatedMessages),
252-
template: currentTemplate,
281+
template: templates,
253282
model: currentModel,
254283
config: languageModel,
255284
})
@@ -279,7 +308,7 @@ export default function Home() {
279308
userID: session?.user?.id,
280309
teamID: userTeam?.id,
281310
messages: toAISDKMessages(messages),
282-
template: currentTemplate,
311+
template: templates,
283312
model: currentModel,
284313
config: languageModel,
285314
})

0 commit comments

Comments
 (0)