Skip to content

Commit eeea47c

Browse files
committed
refactor: centralize error handling in image optimization adapter with unified error classification
1 parent e7a51a1 commit eeea47c

File tree

1 file changed

+40
-83
lines changed

1 file changed

+40
-83
lines changed

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

Lines changed: 40 additions & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ import { resolveImageLoader } from "../core/resolve.js";
3131
import {
3232
FatalError,
3333
IgnorableError,
34-
RecoverableError,
34+
type RecoverableError,
3535
} from "../utils/error.js";
3636
import { debug, error } from "./logger.js";
3737
import { optimizeImage } from "./plugins/image-optimization/image-optimization.js";
@@ -87,7 +87,8 @@ export async function defaultHandler(
8787
// We return a 400 here if imageParams returns an errorMessage
8888
// https://github.com/vercel/next.js/blob/512d8283054407ab92b2583ecce3b253c3be7b85/packages/next/src/server/next-server.ts#L937-L941
8989
if ("errorMessage" in imageParams) {
90-
const clientError = new RecoverableError(imageParams.errorMessage, 400);
90+
// Use IgnorableError for client-side validation issues (logLevel 0) to prevent monitoring alerts
91+
const clientError = new IgnorableError(imageParams.errorMessage, 400);
9192
error("Error during validation of image params", clientError);
9293
return buildFailureResponse(
9394
imageParams.errorMessage,
@@ -137,59 +138,48 @@ export async function defaultHandler(
137138
//////////////////////
138139

139140
/**
140-
* Classifies an error and converts it to the appropriate OpenNext error type
141-
* with the correct status code and logging level.
141+
* Classifies an error as either a client error (IgnorableError) or server error (FatalError)
142+
* to ensure proper logging behavior without triggering false monitoring alerts.
143+
*
144+
* The primary goal is to preserve the original error information while ensuring
145+
* client errors don't trigger monitoring alerts.
142146
*/
143147
function classifyError(e: any): IgnorableError | RecoverableError | FatalError {
144-
// Default values
145-
let statusCode = 500;
146-
const message = e?.message || "Internal server error";
147-
148148
// If it's already an OpenNext error, return it directly
149149
if (e && typeof e === "object" && "__openNextInternal" in e) {
150150
return e;
151151
}
152152

153-
// Determine if this is a client error (4xx) or server error (5xx)
154-
const isClientError =
153+
// Preserve the original message
154+
const message = e?.message || "Internal server error";
155+
156+
// Preserve the original status code if available, otherwise use a default
157+
let statusCode = 500;
158+
if (
155159
e &&
156160
typeof e === "object" &&
157-
(("statusCode" in e &&
158-
typeof e.statusCode === "number" &&
159-
e.statusCode >= 400 &&
160-
e.statusCode < 500) ||
161-
e.code === "ENOTFOUND" ||
162-
e.code === "ECONNREFUSED" ||
163-
(e.message &&
164-
(e.message.includes("403") ||
165-
e.message.includes("404") ||
166-
e.message.includes("Access Denied") ||
167-
e.message.includes("Not Found"))));
168-
169-
// Determine appropriate status code based on error type
170-
if (e && typeof e === "object") {
171-
if ("statusCode" in e && typeof e.statusCode === "number") {
172-
statusCode = e.statusCode;
173-
} else if ("code" in e) {
174-
const code = e.code as string;
175-
if (code === "ENOTFOUND" || code === "ECONNREFUSED") {
176-
statusCode = 404;
177-
}
178-
} else if (e.message) {
179-
if (e.message.includes("403") || e.message.includes("Access Denied")) {
180-
statusCode = 403;
181-
} else if (e.message.includes("404") || e.message.includes("Not Found")) {
182-
statusCode = 404;
183-
}
184-
}
161+
"statusCode" in e &&
162+
typeof e.statusCode === "number"
163+
) {
164+
statusCode = e.statusCode;
185165
}
186166

187-
// Client errors (4xx) are wrapped as IgnorableError to prevent noise in monitoring
188-
if (isClientError || statusCode < 500) {
167+
// Simple check for client errors - anything with a 4xx status code
168+
// or common error codes that indicate client issues
169+
const isClientError =
170+
(statusCode >= 400 && statusCode < 500) ||
171+
(e &&
172+
typeof e === "object" &&
173+
(e.code === "ENOTFOUND" ||
174+
e.code === "ECONNREFUSED" ||
175+
e.code === "ETIMEDOUT"));
176+
177+
// Wrap client errors as IgnorableError to prevent monitoring alerts
178+
if (isClientError) {
189179
return new IgnorableError(message, statusCode);
190180
}
191181

192-
// Server errors (5xx) are marked as FatalError to ensure proper monitoring
182+
// Server errors are marked as FatalError to ensure proper monitoring
193183
return new FatalError(message, statusCode);
194184
}
195185

@@ -413,49 +403,16 @@ async function downloadHandler(
413403
}
414404
}
415405
} catch (e: any) {
416-
// Check if this is a client error (like 404, 403, etc.)
417-
const isClientError =
418-
e &&
419-
typeof e === "object" &&
420-
(("statusCode" in e &&
421-
typeof e.statusCode === "number" &&
422-
e.statusCode >= 400 &&
423-
e.statusCode < 500) ||
424-
e.code === "ENOTFOUND" ||
425-
e.code === "ECONNREFUSED" ||
426-
(e.message &&
427-
(e.message.includes("403") ||
428-
e.message.includes("404") ||
429-
e.message.includes("Access Denied") ||
430-
e.message.includes("Not Found"))));
431-
432-
if (isClientError) {
433-
debug("Client error downloading image", e);
434-
// Just pass through the original error to preserve Next.js's error handling
435-
// but wrap it in IgnorableError to prevent it from being logged as an error
436-
const clientError = new IgnorableError(
437-
e.message || "Client error downloading image",
438-
);
439-
440-
// Preserve the original status code or set an appropriate one
441-
if (e && typeof e === "object") {
442-
if ("statusCode" in e && typeof e.statusCode === "number") {
443-
(clientError as any).statusCode = e.statusCode;
444-
} else if (e.code === "ENOTFOUND" || e.code === "ECONNREFUSED") {
445-
(clientError as any).statusCode = 404;
446-
} else if (e.message?.includes("403")) {
447-
(clientError as any).statusCode = 403;
448-
} else if (e.message?.includes("404")) {
449-
(clientError as any).statusCode = 404;
450-
} else {
451-
(clientError as any).statusCode = 400;
452-
}
453-
}
406+
// Use our centralized error classification function
407+
const classifiedError = classifyError(e);
454408

455-
throw clientError;
456-
}
409+
// Log error with appropriate level based on error type
410+
// This will automatically downgrade client errors to debug level
411+
error("Failed to download image", classifiedError);
457412

458-
error("Failed to download image", e);
459-
throw e;
413+
// Since we're in a middleware (adapter) called by Next.js's image optimizer,
414+
// we should throw the properly classified error to let Next.js handle it appropriately
415+
// The Next.js image optimizer will use the status code and normalize the error message
416+
throw classifiedError;
460417
}
461418
}

0 commit comments

Comments
 (0)