🔧 refactor: customUserVar Error Normalization (#8950)

* fix: localization string had unused template var

* fix: add normalizeHttpError to hopefully stop UI hangs when an error is returned in UserController

- Ensures updateUserPluginsController always returns valid HTTP status codes instead of undefined
- Add normalizeHttpError() helper to safely extract status/message from errors
- Default to 400 status code when Error.status is undefined/invalid

* refactor: move normalizeHttpError to packages/api
This commit is contained in:
Dustin Healy 2025-08-08 12:53:04 -07:00 committed by GitHub
parent 5d0bc95193
commit 9ca1847535
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 36 additions and 8 deletions

View file

@ -1,5 +1,5 @@
const { logger } = require('@librechat/data-schemas');
const { webSearchKeys, extractWebSearchEnvVars } = require('@librechat/api');
const { webSearchKeys, extractWebSearchEnvVars, normalizeHttpError } = require('@librechat/api');
const {
getFiles,
updateUser,
@ -89,8 +89,8 @@ const updateUserPluginsController = async (req, res) => {
if (userPluginsService instanceof Error) {
logger.error('[userPluginsService]', userPluginsService);
const { status, message } = userPluginsService;
res.status(status).send({ message });
const { status, message } = normalizeHttpError(userPluginsService);
return res.status(status).send({ message });
}
}
@ -137,7 +137,7 @@ const updateUserPluginsController = async (req, res) => {
authService = await updateUserPluginAuth(user.id, keys[i], pluginKey, values[i]);
if (authService instanceof Error) {
logger.error('[authService]', authService);
({ status, message } = authService);
({ status, message } = normalizeHttpError(authService));
}
}
} else if (action === 'uninstall') {
@ -151,7 +151,7 @@ const updateUserPluginsController = async (req, res) => {
`[authService] Error deleting all auth for MCP tool ${pluginKey}:`,
authService,
);
({ status, message } = authService);
({ status, message } = normalizeHttpError(authService));
}
} else {
// This handles:
@ -163,7 +163,7 @@ const updateUserPluginsController = async (req, res) => {
authService = await deleteUserPluginAuth(user.id, keys[i]); // Deletes by authField name
if (authService instanceof Error) {
logger.error('[authService] Error deleting specific auth key:', authService);
({ status, message } = authService);
({ status, message } = normalizeHttpError(authService));
}
}
}
@ -193,7 +193,8 @@ const updateUserPluginsController = async (req, res) => {
return res.status(status).send();
}
res.status(status).send({ message });
const normalized = normalizeHttpError({ status, message });
return res.status(normalized.status).send({ message: normalized.message });
} catch (err) {
logger.error('[updateUserPluginsController]', err);
return res.status(500).json({ message: 'Something went wrong.' });

View file

@ -450,7 +450,7 @@
"com_nav_maximize_chat_space": "Maximize chat space",
"com_nav_mcp_configure_server": "Configure {{0}}",
"com_nav_mcp_status_connecting": "{{0}} - Connecting",
"com_nav_mcp_vars_update_error": "Error updating MCP custom user variables: {{0}}",
"com_nav_mcp_vars_update_error": "Error updating MCP custom user variables",
"com_nav_mcp_vars_updated": "MCP custom user variables updated successfully.",
"com_nav_modular_chat": "Enable switching Endpoints mid-conversation",
"com_nav_my_files": "My Files",

View file

@ -0,0 +1,26 @@
/**
* Normalizes an error-like object into an HTTP status and message.
* Ensures we always respond with a valid numeric status to avoid UI hangs.
*/
export function normalizeHttpError(
err: Error | { status?: number; message?: string } | unknown,
fallbackStatus = 400,
) {
let status = fallbackStatus;
if (err && typeof err === 'object' && 'status' in err && typeof err.status === 'number') {
status = err.status;
}
let message = 'An error occurred.';
if (
err &&
typeof err === 'object' &&
'message' in err &&
typeof err.message === 'string' &&
err.message.length > 0
) {
message = err.message;
}
return { status, message };
}

View file

@ -12,3 +12,4 @@ export * from './openid';
export * from './tempChatRetention';
export { default as Tokenizer } from './tokenizer';
export * from './yaml';
export * from './http';