Skip to content

Commit c9f536d

Browse files
committed
feat(image-adapter): improve error handling and status codes for image optimization failures
1 parent ab492ca commit c9f536d

File tree

1 file changed

+74
-2
lines changed

1 file changed

+74
-2
lines changed

packages/open-next/src/adapters/image-optimization-adapter.ts

Lines changed: 74 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -115,9 +115,42 @@ export async function defaultHandler(
115115
return buildSuccessResponse(result, options?.streamCreator, etag);
116116
} catch (e: any) {
117117
error("Failed to optimize image", e);
118+
119+
// Determine appropriate status code based on error
120+
let statusCode = 500; // Default to 500 for unknown errors
121+
let errorMessage = "Internal server error";
122+
123+
// Check if error has HTTP status information
124+
if (e && typeof e === 'object') {
125+
if ('statusCode' in e && typeof e.statusCode === 'number') {
126+
statusCode = e.statusCode;
127+
errorMessage = `HTTP Error: ${statusCode}`;
128+
} else if ('code' in e) {
129+
const code = e.code as string;
130+
if (code === 'ENOTFOUND' || code === 'ECONNREFUSED') {
131+
statusCode = 404;
132+
errorMessage = `Image not found: ${e.message}`;
133+
}
134+
}
135+
136+
if (e.message && typeof e.message === 'string') {
137+
// Try to extract status codes from error messages
138+
if (e.message.includes('403') || e.message.includes('Access Denied')) {
139+
statusCode = 403;
140+
errorMessage = `Access denied: ${e.message}`;
141+
} else if (e.message.includes('404') || e.message.includes('Not Found')) {
142+
statusCode = 404;
143+
errorMessage = `Image not found: ${e.message}`;
144+
} else {
145+
errorMessage = e.message;
146+
}
147+
}
148+
}
149+
118150
return buildFailureResponse(
119-
"Internal server error",
151+
errorMessage,
120152
options?.streamCreator,
153+
statusCode
121154
);
122155
}
123156
}
@@ -255,7 +288,46 @@ async function downloadHandler(
255288
try {
256289
// Case 1: remote image URL => download the image from the URL
257290
if (url.href.toLowerCase().match(/^https?:\/\//)) {
258-
pipeRes(https.get(url), res);
291+
const request = https.get(url, (response) => {
292+
// Check for HTTP error status codes
293+
if (response.statusCode && response.statusCode >= 400) {
294+
error(`Failed to get image: HTTP ${response.statusCode}`);
295+
res.statusCode = response.statusCode;
296+
res.end();
297+
return;
298+
}
299+
// IncomingMessage is a Readable stream, not a Writable
300+
// We need to pipe it directly to the response
301+
response.pipe(res)
302+
.once("close", () => {
303+
if (!res.headersSent) {
304+
res.statusCode = 200;
305+
}
306+
res.end();
307+
})
308+
.once("error", (pipeErr) => {
309+
error("Failed to get image during piping", pipeErr);
310+
if (!res.headersSent) {
311+
res.statusCode = 400;
312+
}
313+
res.end();
314+
});
315+
});
316+
317+
request.on('error', (err: NodeJS.ErrnoException) => {
318+
error("Failed to get image", err);
319+
// Handle common network errors
320+
if (err && typeof err === 'object' && 'code' in err) {
321+
if (err.code === 'ENOTFOUND' || err.code === 'ECONNREFUSED') {
322+
res.statusCode = 404;
323+
} else {
324+
res.statusCode = 400;
325+
}
326+
} else {
327+
res.statusCode = 400;
328+
}
329+
res.end();
330+
});
259331
}
260332
// Case 2: local image => download the image from S3
261333
else {

0 commit comments

Comments
 (0)