Repository: directus/directus
File: api/src/middleware/error-handler.ts (TypeScript)

Code before:
		[key: string]: any;
	};
};

const FALLBACK_ERROR = {
	status: 500,
	message: 'An unexpected error occurred.',
	extensions: {
		code: 'INTERNAL_SERVER_ERROR',
	},
};

export const errorHandler = asyncErrorHandler(async (err, req, res) => {
	const logger = useLogger();

	let errors: ApiError[] = [];
	let status: number | null = null;

	// It can be assumed that at least one error is given
	const receivedErrors: unknown[] = Array.isArray(err) ? err : [err];

	for (const error of receivedErrors) {
		if (getNodeEnv() === 'development') {
			// If available, expose stack trace under error's extensions data
			if (isObject(error) && error['stack']) {
				(error['extensions'] ??= {} as any).stack = error['stack'];
			}
		}

		if (isDirectusError(error)) {
			logger.debug(error);

			if (status === null) {
				// Use current error status as response status
				status = error.status;
			} else if (status !== error.status) {
				// Fallback if status has already been set by a preceding error
				// and doesn't match the current one
				status = FALLBACK_ERROR.status;
			}

			errors.push({
				message: error.message,
				extensions: {
					// Expose error code under error's extensions data
					code: error.code,
					...(error.extensions ?? {}),
				},
			});

			if (isDirectusError(error, ErrorCode.MethodNotAllowed)) {

Review comment (suggestion):
Normally I'm a fan of short handed assignments, but maybe this is more readable?

```suggestion
			if (isObject(error) && error['stack'] && (error['extensions'] === undefined || isObject(error['extensions']))) {
				error['extensions'] = {
					...error['extensions'],
					stack: error['stack'],
				}
```

Code after:
		code: string;
		[key: string]: any;
	};
};

const FALLBACK_ERROR = new InternalServerError();

export const errorHandler = asyncErrorHandler(async (err, req, res) => {
	const logger = useLogger();

	let errors: ApiError[] = [];
	let status: number | null = null;

	// It can be assumed that at least one error is given
	const receivedErrors: unknown[] = Array.isArray(err) ? err : [err];

	for (const error of receivedErrors) {
		// In dev mode, if available, expose stack trace under error's extensions data
		if (getNodeEnv() === 'development' && error instanceof Error && error.stack) {
			((error as DeepPartial<ApiError>).extensions ??= {})['stack'] = error.stack;
		}

		if (isDirectusError(error)) {
			logger.debug(error);

			if (status === null) {
				// Use current error status as response status
				status = error.status;
			} else if (status !== error.status) {
				// Fallback if status has already been set by a preceding error
				// and doesn't match the current one
				status = FALLBACK_ERROR.status;
			}

			errors.push({
				message: error.message,
				extensions: {
					...(error.extensions ?? {}),
					// Expose error code under error's extensions data
					code: error.code,
				},
			});

			if (isDirectusError(error, ErrorCode.MethodNotAllowed)) {
				res.header('Allow', error.extensions.allowed.join(', '));
			}
		} else {
			logger.error(error);

			status = FALLBACK_ERROR.status;