mirror of
https://github.com/pewdiepie-archdaemon/odysseus.git
synced 2026-06-16 17:55:26 -04:00
chore: add warnings to silent except Exception blocks (#3212)
* log(app): add warnings to silent except Exception blocks - Internal tool auth header failure now logs a warning instead of silently passing, making auth bypass easier to spot in logs. - Token last_used_at update failure now logs at DEBUG (fire-and-forget, non-critical, but useful when debugging token tracking issues). - Image ownership verification failure now logs a warning so unexpected access-check errors surface instead of silently allowing the request. * log(chat_routes): add warnings to silent except Exception blocks - clear_orphaned_session_endpoint: log before rollback so failures appear in traces when users see stale/deleted model options. - _endpoint_has_model (JSON parse): log malformed cached_models instead of silently treating endpoint as valid. - _has_any_visible_model (JSON parse): log malformed cached_models instead of silently returning empty list. - timezone header parse: log failure so time-zone-related tool bugs (wrong scheduled times, calendar events) are traceable. - attachments JSON parse: log failure so silently-dropped attachments are visible in server logs. * log(email_routes): add warnings to silent except Exception blocks - Email alias resolution failure now logs a warning instead of silently returning an empty list, making broken account configs diagnosable. * log(document_routes): add warnings to silent except Exception blocks - Export ZIP request body parse failure now logs a warning so empty exports caused by malformed requests are diagnosable. - clear_active_document failure on detach now logs a warning to help trace doc re-injection bugs like #1160. * log(agent_loop): add warnings to silent except Exception blocks - builtin tool overrides load failure now logs a warning so misconfigured settings don't silently fall back to defaults without a trace. - Timezone context injection failure now logs a warning to help debug incorrect scheduled times in agent-created tasks. - PDF form-backed document detection failure now logs a warning so broken form-doc UI is traceable to the root cause. * log(llm_core): add warnings to silent except Exception blocks - Malformed URL in _is_ollama_native_url now logs a warning so bad endpoint configs are traceable instead of silently returning False. - Model list fetch failure now logs a warning with the endpoint URL so endpoints that silently vanish from the model picker are diagnosable. * log: pass exception via exc_info instead of string interpolation * fix(logging): avoid logging raw URLs in llm_core error paths Drop the raw url/base_chat_url from the Ollama-detection and model-list-fetch warning logs added by this sweep, since these values can contain private hostnames, internal IPs, credentials, or other deployment details. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -331,8 +331,8 @@ if AUTH_ENABLED:
|
|||||||
request.state.current_user = "internal-tool"
|
request.state.current_user = "internal-tool"
|
||||||
request.state.api_token = False
|
request.state.api_token = False
|
||||||
return await call_next(request)
|
return await call_next(request)
|
||||||
except Exception:
|
except Exception as _e:
|
||||||
pass
|
logger.warning("Internal tool auth header check failed", exc_info=_e)
|
||||||
# Allow DIRECT localhost requests (internal service calls from
|
# Allow DIRECT localhost requests (internal service calls from
|
||||||
# heartbeats etc.). Tunnel/proxy-forwarded requests are excluded by
|
# heartbeats etc.). Tunnel/proxy-forwarded requests are excluded by
|
||||||
# _is_trusted_loopback so LOCALHOST_BYPASS can't be abused over a
|
# _is_trusted_loopback so LOCALHOST_BYPASS can't be abused over a
|
||||||
@@ -385,11 +385,10 @@ if AUTH_ENABLED:
|
|||||||
_db.close()
|
_db.close()
|
||||||
try:
|
try:
|
||||||
await _asyncio.to_thread(_do)
|
await _asyncio.to_thread(_do)
|
||||||
except Exception:
|
except Exception as _e:
|
||||||
pass
|
logger.debug("Failed to update token last_used_at", exc_info=_e)
|
||||||
_asyncio.create_task(_touch_last_used(matched_id))
|
_asyncio.create_task(_touch_last_used(matched_id))
|
||||||
# Keep bearer-token callers out of normal cookie/user
|
# Keep bearer-token callers out of normal cookie/user
|
||||||
# routes. API-aware routes can read api_token_owner.
|
|
||||||
request.state.current_user = "api"
|
request.state.current_user = "api"
|
||||||
request.state.api_token = True
|
request.state.api_token = True
|
||||||
request.state.api_token_id = matched_id
|
request.state.api_token_id = matched_id
|
||||||
@@ -464,8 +463,8 @@ async def serve_generated_image(filename: str, request: Request):
|
|||||||
_db.close()
|
_db.close()
|
||||||
except HTTPException:
|
except HTTPException:
|
||||||
raise
|
raise
|
||||||
except Exception:
|
except Exception as _e:
|
||||||
pass
|
logger.warning("Image ownership verification failed for %r", filename, exc_info=_e)
|
||||||
ext = filename.rsplit('.', 1)[-1].lower()
|
ext = filename.rsplit('.', 1)[-1].lower()
|
||||||
mime = {
|
mime = {
|
||||||
"png": "image/png", "jpg": "image/jpeg", "jpeg": "image/jpeg",
|
"png": "image/png", "jpg": "image/jpeg", "jpeg": "image/jpeg",
|
||||||
|
|||||||
@@ -126,7 +126,8 @@ def _clear_orphaned_session_endpoint(sess, owner: str | None = None) -> bool:
|
|||||||
sess.model = ""
|
sess.model = ""
|
||||||
sess.headers = {}
|
sess.headers = {}
|
||||||
return True
|
return True
|
||||||
except Exception:
|
except Exception as e:
|
||||||
|
logger.warning("Failed to clear orphaned session endpoint", exc_info=e)
|
||||||
db.rollback()
|
db.rollback()
|
||||||
return False
|
return False
|
||||||
finally:
|
finally:
|
||||||
@@ -144,7 +145,8 @@ def _endpoint_cache_contains_model(endpoint, model: str) -> bool:
|
|||||||
return True
|
return True
|
||||||
try:
|
try:
|
||||||
models = json.loads(raw) if isinstance(raw, str) else raw
|
models = json.loads(raw) if isinstance(raw, str) else raw
|
||||||
except Exception:
|
except Exception as e:
|
||||||
|
logger.warning("Failed to parse cached models list, treating as containing model", exc_info=e)
|
||||||
return True
|
return True
|
||||||
if not isinstance(models, list) or not models:
|
if not isinstance(models, list) or not models:
|
||||||
return True
|
return True
|
||||||
@@ -236,7 +238,8 @@ def _recover_empty_session_model(sess, session_id: str, owner: str | None = None
|
|||||||
is_chatgpt_subscription = False
|
is_chatgpt_subscription = False
|
||||||
try:
|
try:
|
||||||
cached = json.loads(ep.cached_models) if isinstance(ep.cached_models, str) else (ep.cached_models or [])
|
cached = json.loads(ep.cached_models) if isinstance(ep.cached_models, str) else (ep.cached_models or [])
|
||||||
except Exception:
|
except Exception as e:
|
||||||
|
logger.warning("Failed to parse cached_models for endpoint %r", getattr(ep, "id", "?"), exc_info=e)
|
||||||
cached = []
|
cached = []
|
||||||
if not cached:
|
if not cached:
|
||||||
visible = []
|
visible = []
|
||||||
@@ -646,8 +649,8 @@ def setup_chat_routes(
|
|||||||
elif attachments:
|
elif attachments:
|
||||||
try:
|
try:
|
||||||
att_ids = [str(x) for x in json.loads(attachments)]
|
att_ids = [str(x) for x in json.loads(attachments)]
|
||||||
except Exception:
|
except Exception as e:
|
||||||
pass
|
logger.warning("Failed to parse attachments JSON, ignoring attachments", exc_info=e)
|
||||||
|
|
||||||
no_memory = str(form_data.get("no_memory", "")).lower() == "true"
|
no_memory = str(form_data.get("no_memory", "")).lower() == "true"
|
||||||
pre_context_tool_policy = build_effective_tool_policy(
|
pre_context_tool_policy = build_effective_tool_policy(
|
||||||
|
|||||||
@@ -503,7 +503,8 @@ def setup_document_routes(session_manager, upload_handler=None) -> APIRouter:
|
|||||||
user = get_current_user(request)
|
user = get_current_user(request)
|
||||||
try:
|
try:
|
||||||
data = await request.json()
|
data = await request.json()
|
||||||
except Exception:
|
except Exception as e:
|
||||||
|
logger.warning("Failed to parse export request body, defaulting to empty", exc_info=e)
|
||||||
data = {}
|
data = {}
|
||||||
ids = data.get("ids") or []
|
ids = data.get("ids") or []
|
||||||
if not ids:
|
if not ids:
|
||||||
@@ -645,8 +646,8 @@ def setup_document_routes(session_manager, upload_handler=None) -> APIRouter:
|
|||||||
try:
|
try:
|
||||||
from src.agent_tools.document_tools import clear_active_document
|
from src.agent_tools.document_tools import clear_active_document
|
||||||
clear_active_document(doc_id)
|
clear_active_document(doc_id)
|
||||||
except Exception:
|
except Exception as e:
|
||||||
pass
|
logger.warning("Failed to clear active document %r on detach", doc_id, exc_info=e)
|
||||||
db.commit()
|
db.commit()
|
||||||
db.refresh(doc)
|
db.refresh(doc)
|
||||||
return _doc_to_dict(doc)
|
return _doc_to_dict(doc)
|
||||||
|
|||||||
@@ -79,15 +79,16 @@ def _email_tag_owner_aliases(account_id: str | None, owner: str = "") -> list[st
|
|||||||
cfg.get("smtp_user") or "",
|
cfg.get("smtp_user") or "",
|
||||||
cfg.get("from_address") or "",
|
cfg.get("from_address") or "",
|
||||||
])
|
])
|
||||||
except Exception:
|
except Exception as _e:
|
||||||
|
logger.warning("Failed to resolve email account alias", exc_info=_e)
|
||||||
resolved_account_id = None
|
resolved_account_id = None
|
||||||
row = db.get(_EA, resolved_account_id) if resolved_account_id else None
|
row = db.get(_EA, resolved_account_id) if resolved_account_id else None
|
||||||
if row:
|
if row:
|
||||||
aliases.extend([row.owner or "", row.imap_user or "", row.from_address or ""])
|
aliases.extend([row.owner or "", row.imap_user or "", row.from_address or ""])
|
||||||
finally:
|
finally:
|
||||||
db.close()
|
db.close()
|
||||||
except Exception:
|
except Exception as _e:
|
||||||
pass
|
logger.warning("Failed to load email aliases", exc_info=_e)
|
||||||
out = []
|
out = []
|
||||||
for a in aliases:
|
for a in aliases:
|
||||||
a = (a or "").strip()
|
a = (a or "").strip()
|
||||||
|
|||||||
+5
-5
@@ -524,7 +524,7 @@ def get_builtin_overrides() -> dict:
|
|||||||
ov = get_setting("builtin_tool_overrides", {})
|
ov = get_setting("builtin_tool_overrides", {})
|
||||||
return ov if isinstance(ov, dict) else {}
|
return ov if isinstance(ov, dict) else {}
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.warning('Failed to load builtin tool overrides: %s', e)
|
logger.warning("Failed to load builtin tool overrides, using defaults", exc_info=e)
|
||||||
return {}
|
return {}
|
||||||
|
|
||||||
|
|
||||||
@@ -929,8 +929,8 @@ def _build_system_prompt(
|
|||||||
try:
|
try:
|
||||||
from src.user_time import current_datetime_context_message
|
from src.user_time import current_datetime_context_message
|
||||||
_datetime_message = current_datetime_context_message()
|
_datetime_message = current_datetime_context_message()
|
||||||
except Exception:
|
except Exception as e:
|
||||||
pass
|
logger.warning("Failed to build datetime context message", exc_info=e)
|
||||||
|
|
||||||
# Document context is kept as a SEPARATE message (not merged into the tool
|
# Document context is kept as a SEPARATE message (not merged into the tool
|
||||||
# prompt) so the context trimmer doesn't destroy it when truncating the
|
# prompt) so the context trimmer doesn't destroy it when truncating the
|
||||||
@@ -973,8 +973,8 @@ def _build_system_prompt(
|
|||||||
try:
|
try:
|
||||||
from src.pdf_form_doc import find_source_upload_id
|
from src.pdf_form_doc import find_source_upload_id
|
||||||
_is_form_backed = bool(find_source_upload_id(active_document.current_content or ""))
|
_is_form_backed = bool(find_source_upload_id(active_document.current_content or ""))
|
||||||
except Exception:
|
except Exception as e:
|
||||||
pass
|
logger.warning("Failed to detect if document is form-backed, assuming plain", exc_info=e)
|
||||||
|
|
||||||
if _is_form_backed:
|
if _is_form_backed:
|
||||||
doc_ctx = (
|
doc_ctx = (
|
||||||
|
|||||||
+4
-3
@@ -283,7 +283,8 @@ def _is_ollama_native_url(url: str) -> bool:
|
|||||||
"""Return True for native Ollama API URLs, including Ollama Cloud."""
|
"""Return True for native Ollama API URLs, including Ollama Cloud."""
|
||||||
try:
|
try:
|
||||||
parsed = urlparse(url or "")
|
parsed = urlparse(url or "")
|
||||||
except Exception:
|
except Exception as e:
|
||||||
|
logger.warning("Failed to parse URL for Ollama detection", exc_info=e)
|
||||||
return False
|
return False
|
||||||
host = parsed.hostname or ""
|
host = parsed.hostname or ""
|
||||||
path = (parsed.path or "").rstrip("/")
|
path = (parsed.path or "").rstrip("/")
|
||||||
@@ -1345,8 +1346,8 @@ def list_model_ids(
|
|||||||
r = httpx.get(root + "/api/tags", timeout=timeout)
|
r = httpx.get(root + "/api/tags", timeout=timeout)
|
||||||
r.raise_for_status()
|
r.raise_for_status()
|
||||||
return [m.get("name") or m.get("model") for m in (r.json().get("models") or []) if m.get("name") or m.get("model")]
|
return [m.get("name") or m.get("model") for m in (r.json().get("models") or []) if m.get("name") or m.get("model")]
|
||||||
except Exception:
|
except Exception as e:
|
||||||
pass
|
logger.warning("Failed to fetch model list from configured endpoint", exc_info=e)
|
||||||
return []
|
return []
|
||||||
|
|
||||||
def normalize_model_id(
|
def normalize_model_id(
|
||||||
|
|||||||
Reference in New Issue
Block a user