From e4739a17f370954a3ff4e9434fb04d43b4b3784a Mon Sep 17 00:00:00 2001 From: auctor Date: Wed, 22 Apr 2026 23:37:30 +0000 Subject: [PATCH] fix(utils): re-raise in timed_with_status when no fallback is set The except branch in timed_with_status caught every exception, ran the optional fallback if configured, but otherwise let the function fall through to the implicit return None. Decorated functions like OpenAILLM.generate then returned None on a 4xx/5xx without surfacing the underlying error, and callers crashed later with confusing AttributeErrors (e.g. clean_json_response receiving None). Add an explicit `raise` so the failure propagates when no fallback is configured. Existing fallback semantics are unchanged. Repro: configure MOS_CHAT_MODEL=MiniMax-M2.7 with OPENAI_API_BASE pointed at the MiniMax v1 endpoint, then POST /product/suggestions with a body whose suggestion prompt contains only a system message. MiniMax replies 400 'chat content is empty (2013)', the decorator swallows the BadRequestError, generate() returns None, and suggestion_handler then dies in clean_json_response with 'NoneType object has no attribute replace'. With this patch the BadRequestError reaches the handler intact and the user sees the real 400 instead of a misleading AttributeError. --- src/memos/utils.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/memos/utils.py b/src/memos/utils.py index fd6d4eaf9..3796371af 100644 --- a/src/memos/utils.py +++ b/src/memos/utils.py @@ -53,6 +53,11 @@ def wrapper(*args, **kwargs): if fallback is not None and callable(fallback): result = fallback(e, *args, **kwargs) return result + # No fallback configured -> re-raise so callers see the failure + # instead of receiving an implicit None. Otherwise downstream + # parsers (e.g. clean_json_response) crash with confusing + # AttributeErrors that hide the real LLM/HTTP error. + raise finally: elapsed_ms = (time.perf_counter() - start) * 1000.0