{"record":{"id":"71cabeb570d7f153","repo":"odysseus-dev/odysseus","slug":"daily-message-limit-reached-cap-try-again-in","errorCode":null,"errorMessage":"Daily message limit reached ({cap}). Try again in 24 hours.","messagePattern":"Daily message limit reached \\((.+?)\\)\\. Try again in 24 hours\\.","errorType":"http","errorClass":"HTTPException","httpStatus":429,"severity":"error","filePath":"routes/chat_helpers.py","lineNumber":239,"sourceCode":"    if cap <= 0:\n        return\n\n    from datetime import datetime as _dt, timedelta as _td\n    from core.database import Session as _DbSess, ChatMessage as _Cm\n    db = SessionLocal()\n    try:\n        count = (\n            db.query(_Cm)\n            .join(_DbSess, _Cm.session_id == _DbSess.id)\n            .filter(_DbSess.owner == user,\n                    _Cm.role == \"user\",\n                    _Cm.timestamp >= _dt.utcnow() - _td(days=1))\n            .count()\n        )\n    finally:\n        db.close()\n    if count >= cap:\n        raise HTTPException(429, f\"Daily message limit reached ({cap}). Try again in 24 hours.\")\n\n\ndef needs_auto_name(name: str) -> bool:\n    \"\"\"Check if a session still has its default/placeholder name.\"\"\"\n    if not name:\n        return True\n    if name.startswith(\"Chat:\") or name == \"Chat\":\n        return True\n    # Default frontend name: \"modelname HH:MM:SS AM/PM\"\n    if re.match(r\"^.+ \\d{1,2}:\\d{2}:\\d{2}(\\s*(AM|PM))?$\", name, re.IGNORECASE):\n        return True\n    return False\n\n\nasync def auto_name_session(session_manager, sess):\n    \"\"\"Generate a short title for a session from its first user message.\"\"\"\n    try:\n        from src.llm_core import llm_call_async","sourceCodeStart":221,"sourceCodeEnd":257,"githubUrl":"https://github.com/odysseus-dev/odysseus/blob/f9235ebbf13f693a6fd29ce70b097f6ec83705bf/routes/chat_helpers.py#L221-L257","documentation":"A usage-quota gate that counts the user's own user-role chat messages across all their sessions in the trailing 24 UTC hours and rejects with HTTP 429 when the count reaches the max_messages_per_day cap. It fires before any LLM work so no tokens are spent over quota.","triggerScenarios":"User at or over their daily cap sends any chat message. Each user-role row in ChatMessage joined to a session owned by the user within the last day counts toward cap.","commonSituations":"Free-tier user with a low cap hitting it mid-conversation; automated scripts posting messages that burn the quota; cap set very low (e.g. 10) by an admin for testing and never raised.","solutions":["Wait until messages age out of the 24-hour UTC window (quota is rolling, not calendar-day)","Ask an admin to raise or remove max_messages_per_day for the user","Audit for scripted or duplicated sends inflating the count"],"exampleFix":null,"handlingStrategy":"validation","validationCode":"// Before sending, check remaining quota if the API exposes it; otherwise track sends client-side\nif (messagesSentToday >= knownCap) { showQuotaExceededUI(retryAt: nextUtcRollout); return; }","typeGuard":null,"tryCatchPattern":"try { await sendChat(...); } catch (e) {\n  if (e.status === 429 && /Daily message limit/.test(e.message)) { disableComposeUntilWindowRolls(); }\n}","preventionTips":["Show a remaining-messages counter when a cap is configured","Debounce/deduplicate send actions so double-clicks don't burn quota","Respect Retry-After-style backoff instead of hammering the endpoint"],"tags":["rate-limit","quota","http-429","chat"],"backgroundTag":null,"analyzedSha":"f9235ebbf13f693a6fd29ce70b097f6ec83705bf","analyzedAt":"2026-08-14T21:47:48.359Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}