Skip to content

Commit d42e5a7

Browse files
Merge pull request Gerome-Elassaad#29 from Gerome-Elassaad/fix/messaging-errors
Fix messaging errors and improve error handling
2 parents 16f2d2e + d778242 commit d42e5a7

File tree

7 files changed

+300
-215
lines changed

7 files changed

+300
-215
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: 136 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -7,73 +7,149 @@ 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+
} else if (fragment.code !== null && fragment.code !== undefined) {
85+
await sbx.files.write(fragment.file_path, fragment.code)
86+
} else {
87+
return new Response(
88+
JSON.stringify({
89+
error: 'Missing code data',
90+
type: 'validation_error'
91+
}),
92+
{ status: 400, headers: { 'Content-Type': 'application/json' } }
93+
)
94+
}
95+
96+
if (fragment.template === 'code-interpreter-v1') {
97+
const { logs, error, results } = await sbx.runCode(fragment.code || '')
5098

51-
if (fragment.template === 'code-interpreter-v1') {
52-
const { logs, error, results } = await sbx.runCode(fragment.code || '')
99+
return new Response(
100+
JSON.stringify({
101+
sbxId: sbx?.sandboxId,
102+
template: fragment.template,
103+
stdout: logs.stdout,
104+
stderr: logs.stderr,
105+
runtimeError: error,
106+
cellResults: results,
107+
} as ExecutionResultInterpreter),
108+
{ headers: { 'Content-Type': 'application/json' } }
109+
)
110+
}
53111

112+
await sbx.commands.run(fragment.install_dependencies_command, {
113+
envs: {
114+
PORT: (fragment.port || 80).toString(),
115+
},
116+
})
117+
118+
return new Response(
119+
JSON.stringify({
120+
sbxId: sbx?.sandboxId,
121+
template: fragment.template,
122+
url: `https://${sbx?.getHost(fragment.port || 80)}`,
123+
} as ExecutionResultWeb),
124+
{ headers: { 'Content-Type': 'application/json' } }
125+
)
126+
} catch (executionError: any) {
127+
console.error('Sandbox execution error:', executionError)
128+
129+
// Clean up sandbox on execution error
130+
try {
131+
await sbx?.kill()
132+
} catch {}
133+
134+
return new Response(
135+
JSON.stringify({
136+
error: 'Code execution failed. There may be an error in your code or dependencies.',
137+
type: 'execution_error',
138+
details: executionError.message
139+
}),
140+
{ status: 500, headers: { 'Content-Type': 'application/json' } }
141+
)
142+
}
143+
144+
} catch (error: any) {
145+
console.error('Sandbox API Error:', error)
54146
return new Response(
55147
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),
148+
error: 'An unexpected error occurred while setting up the sandbox.',
149+
type: 'unknown_error',
150+
details: error?.message || 'Unknown error'
151+
}),
152+
{ status: 500, headers: { 'Content-Type': 'application/json' } }
63153
)
64154
}
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-
)
79155
}

0 commit comments

Comments
 (0)