diff --git a/packages/next/build/webpack/loaders/next-serverless-loader/api-handler.ts b/packages/next/build/webpack/loaders/next-serverless-loader/api-handler.ts index 71c050ba0e1ee..6171c86a01f59 100644 --- a/packages/next/build/webpack/loaders/next-serverless-loader/api-handler.ts +++ b/packages/next/build/webpack/loaders/next-serverless-loader/api-handler.ts @@ -2,6 +2,7 @@ import { parse as parseUrl } from 'url' import { IncomingMessage, ServerResponse } from 'http' import { apiResolver } from '../../../../server/api-utils' import { getUtils, vercelHeader, ServerlessHandlerCtx } from './utils' +import { DecodeError } from '../../../../shared/lib/utils' export function getApiHandler(ctx: ServerlessHandlerCtx) { const { pageModule, encodedPreviewProps, pageIsDynamic } = ctx @@ -50,8 +51,7 @@ export function getApiHandler(ctx: ServerlessHandlerCtx) { } catch (err) { console.error(err) - // TODO: better error for DECODE_FAILED? - if (err.code === 'DECODE_FAILED') { + if (err instanceof DecodeError) { res.statusCode = 400 res.end('Bad Request') } else { diff --git a/packages/next/build/webpack/loaders/next-serverless-loader/page-handler.ts b/packages/next/build/webpack/loaders/next-serverless-loader/page-handler.ts index a09f276f17471..1c9e97f77d952 100644 --- a/packages/next/build/webpack/loaders/next-serverless-loader/page-handler.ts +++ b/packages/next/build/webpack/loaders/next-serverless-loader/page-handler.ts @@ -1,6 +1,6 @@ import { IncomingMessage, ServerResponse } from 'http' import { parse as parseUrl, format as formatUrl, UrlWithParsedQuery } from 'url' -import { isResSent } from '../../../../shared/lib/utils' +import { DecodeError, isResSent } from '../../../../shared/lib/utils' import { sendPayload } from '../../../../server/send-payload' import { getUtils, vercelHeader, ServerlessHandlerCtx } from './utils' @@ -409,8 +409,7 @@ export function getPageHandler(ctx: ServerlessHandlerCtx) { if (err.code === 'ENOENT') { res.statusCode = 404 - } else if (err.code === 'DECODE_FAILED' || err.code === 'ENAMETOOLONG') { - // TODO: better error? + } else if (err instanceof DecodeError) { res.statusCode = 400 } else { console.error('Unhandled error during request:', err) diff --git a/packages/next/server/dev/hot-reloader.ts b/packages/next/server/dev/hot-reloader.ts index 255a5d0ea8c16..4274743cb73f3 100644 --- a/packages/next/server/dev/hot-reloader.ts +++ b/packages/next/server/dev/hot-reloader.ts @@ -26,6 +26,7 @@ import { stringify } from 'querystring' import { difference } from '../../build/utils' import { NextConfig } from '../config' import { CustomRoutes } from '../../lib/load-custom-routes' +import { DecodeError } from '../../shared/lib/utils' export async function renderScriptError( res: ServerResponse, @@ -212,11 +213,7 @@ export default class HotReloader { .map((param) => decodeURIComponent(param)) .join('/')}` } catch (_) { - const err: Error & { code?: string } = new Error( - 'failed to decode param' - ) - err.code = 'DECODE_FAILED' - throw err + throw new DecodeError('failed to decode param') } const page = denormalizePagePath(decodedPagePath) diff --git a/packages/next/server/dev/next-dev-server.ts b/packages/next/server/dev/next-dev-server.ts index f882ac91e968b..f95f1e8d7bfaa 100644 --- a/packages/next/server/dev/next-dev-server.ts +++ b/packages/next/server/dev/next-dev-server.ts @@ -46,6 +46,7 @@ import { LoadComponentsReturnType, loadDefaultErrorComponents, } from '../load-components' +import { DecodeError } from '../../shared/lib/utils' if (typeof React.Suspense === 'undefined') { throw new Error( @@ -376,9 +377,7 @@ export default class DevServer extends Server { try { decodedPath = decodeURIComponent(path) } catch (_) { - const err: Error & { code?: string } = new Error('failed to decode param') - err.code = 'DECODE_FAILED' - throw err + throw new DecodeError('failed to decode param') } if (await this.hasPublicFile(decodedPath)) { diff --git a/packages/next/server/next-server.ts b/packages/next/server/next-server.ts index 712fd7c4c0f6b..28d8f0fdf95da 100644 --- a/packages/next/server/next-server.ts +++ b/packages/next/server/next-server.ts @@ -41,7 +41,12 @@ import { isDynamicRoute, } from '../shared/lib/router/utils' import * as envConfig from '../shared/lib/runtime-config' -import { isResSent, NextApiRequest, NextApiResponse } from '../shared/lib/utils' +import { + DecodeError, + isResSent, + NextApiRequest, + NextApiResponse, +} from '../shared/lib/utils' import { apiResolver, setLazyProp, @@ -1276,7 +1281,7 @@ export default class Server { return } } catch (err) { - if (err.code === 'DECODE_FAILED' || err.code === 'ENAMETOOLONG') { + if (err instanceof DecodeError) { res.statusCode = 400 return this.renderError(null, req, res, '/_error', {}) } @@ -1381,16 +1386,15 @@ export default class Server { pagePath!, !this.renderOpts.dev && this._isLikeServerless ) - // if loading an static HTML file the locale is required - // to be present since all HTML files are output under their locale + if ( query.__nextLocale && typeof components.Component === 'string' && !pagePath?.startsWith(`/${query.__nextLocale}`) ) { - const err = new Error('NOT_FOUND') - ;(err as any).code = 'ENOENT' - throw err + // if loading an static HTML file the locale is required + // to be present since all HTML files are output under their locale + continue } return { @@ -1589,13 +1593,8 @@ export default class Server { try { seg = escapePathDelimiters(decodeURIComponent(seg), true) } catch (_) { - // An improperly encoded URL was provided, this is considered - // a bad request (400) - const err: Error & { code?: string } = new Error( - 'failed to decode param' - ) - err.code = 'DECODE_FAILED' - throw err + // An improperly encoded URL was provided + throw new DecodeError('failed to decode param') } return seg }) @@ -1962,16 +1961,14 @@ export default class Server { } } } catch (err) { - const isNoFallbackError = err instanceof NoFallbackError - - if (isNoFallbackError && bubbleNoFallback) { + if (err instanceof NoFallbackError && bubbleNoFallback) { throw err } - - if (err && err.code === 'DECODE_FAILED') { + if (err instanceof DecodeError) { res.statusCode = 400 return await this.renderErrorToHTML(err, req, res, pathname, query) } + res.statusCode = 500 const isWrappedError = err instanceof WrappedBuildError const html = await this.renderErrorToHTML( diff --git a/packages/next/shared/lib/router/utils/route-matcher.ts b/packages/next/shared/lib/router/utils/route-matcher.ts index b3faafb1b2af9..22a6c85bda7d2 100644 --- a/packages/next/shared/lib/router/utils/route-matcher.ts +++ b/packages/next/shared/lib/router/utils/route-matcher.ts @@ -1,3 +1,4 @@ +import { DecodeError } from '../../utils' import { getRouteRegex } from './route-regex' export function getRouteMatcher(routeRegex: ReturnType) { @@ -12,11 +13,7 @@ export function getRouteMatcher(routeRegex: ReturnType) { try { return decodeURIComponent(param) } catch (_) { - const err: Error & { code?: string } = new Error( - 'failed to decode param' - ) - err.code = 'DECODE_FAILED' - throw err + throw new DecodeError('failed to decode param') } } const params: { [paramName: string]: string | string[] } = {} diff --git a/packages/next/shared/lib/utils.ts b/packages/next/shared/lib/utils.ts index 499ca94bab122..0a3ffdbfbc87b 100644 --- a/packages/next/shared/lib/utils.ts +++ b/packages/next/shared/lib/utils.ts @@ -406,3 +406,5 @@ export const ST = SP && typeof performance.mark === 'function' && typeof performance.measure === 'function' + +export class DecodeError extends Error {}