@@ -31,7 +31,7 @@ import { resolveImageLoader } from "../core/resolve.js";
31
31
import {
32
32
FatalError ,
33
33
IgnorableError ,
34
- RecoverableError ,
34
+ type RecoverableError ,
35
35
} from "../utils/error.js" ;
36
36
import { debug , error } from "./logger.js" ;
37
37
import { optimizeImage } from "./plugins/image-optimization/image-optimization.js" ;
@@ -87,7 +87,8 @@ export async function defaultHandler(
87
87
// We return a 400 here if imageParams returns an errorMessage
88
88
// https://github.com/vercel/next.js/blob/512d8283054407ab92b2583ecce3b253c3be7b85/packages/next/src/server/next-server.ts#L937-L941
89
89
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 ) ;
91
92
error ( "Error during validation of image params" , clientError ) ;
92
93
return buildFailureResponse (
93
94
imageParams . errorMessage ,
@@ -137,59 +138,48 @@ export async function defaultHandler(
137
138
//////////////////////
138
139
139
140
/**
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.
142
146
*/
143
147
function classifyError ( e : any ) : IgnorableError | RecoverableError | FatalError {
144
- // Default values
145
- let statusCode = 500 ;
146
- const message = e ?. message || "Internal server error" ;
147
-
148
148
// If it's already an OpenNext error, return it directly
149
149
if ( e && typeof e === "object" && "__openNextInternal" in e ) {
150
150
return e ;
151
151
}
152
152
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 (
155
159
e &&
156
160
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 ;
185
165
}
186
166
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 ) {
189
179
return new IgnorableError ( message , statusCode ) ;
190
180
}
191
181
192
- // Server errors (5xx) are marked as FatalError to ensure proper monitoring
182
+ // Server errors are marked as FatalError to ensure proper monitoring
193
183
return new FatalError ( message , statusCode ) ;
194
184
}
195
185
@@ -413,49 +403,16 @@ async function downloadHandler(
413
403
}
414
404
}
415
405
} 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 ) ;
454
408
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 ) ;
457
412
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 ;
460
417
}
461
418
}
0 commit comments