{"record":{"id":"a42115d09d1c1770","repo":"Significant-Gravitas/AutoGPT","slug":"chat-service-degraded-retry-shortly","errorCode":null,"errorMessage":"Chat service degraded, retry shortly","messagePattern":"Chat service degraded, retry shortly","errorType":"http","errorClass":"HTTPException","httpStatus":503,"severity":"error","filePath":"autogpt_platform/backend/backend/api/features/chat/routes.py","lineNumber":1411,"sourceCode":"    # Session-anchored tenancy: the ChatSession row is the authoritative\n    # org/team for every turn in it — a user whose active header org\n    # differs still charges/attributes turns to the session's org.\n    # Untagged legacy sessions fall back to the request context.\n    turn_org_id = session.organization_id or ctx.org_id\n    turn_team_id = session.team_id if session.organization_id else ctx.team_id\n\n    try:\n        turn_in_flight = (\n            request.is_user_message\n            and request.message\n            and await is_turn_in_flight(session_id)\n        )\n    except StreamRegistryUnavailable as exc:\n        # Same fail-closed mapping as the RateLimitUnavailable branch below:\n        # the pre-flight chain runs is_turn_in_flight BEFORE check_rate_limit,\n        # so a Redis brown-out at this step would otherwise surface as a raw\n        # 500 instead of the polished 503 + Retry-After.\n        raise HTTPException(\n            status_code=503,\n            detail=\"Chat service degraded, retry shortly\",\n            headers={\"Retry-After\": \"30\"},\n        ) from exc\n\n    if turn_in_flight:\n        try:\n            await queue_pending_for_http(\n                session_id=session_id,\n                user_id=user_id,\n                message=request.message,\n                context=request.context,\n                file_ids=request.file_ids,\n            )\n            return _empty_ui_message_stream_response()\n        except HTTPException as exc:\n            if exc.status_code != 409:\n                raise","sourceCodeStart":1393,"sourceCodeEnd":1429,"githubUrl":"https://github.com/Significant-Gravitas/AutoGPT/blob/9c8bb5550f446ba5d3046b78896578742495b3cf/autogpt_platform/backend/backend/api/features/chat/routes.py#L1393-L1429","documentation":"HTTP 503 with header Retry-After: 30 from POST /chat/stream (routes.py:1411). Before enqueueing a turn, the endpoint checks is_turn_in_flight(session_id) against the stream registry backed by Redis. StreamRegistryUnavailable means Redis could not answer. As the source comment explains, this pre-flight step runs before check_rate_limit, so the branch maps the failure to a polished 503 + Retry-After instead of a raw 500.","triggerScenarios":"POST /chat/stream with is_user_message and a non-empty message while Redis (stream registry) is unavailable — raise of StreamRegistryUnavailable from is_turn_in_flight(session_id).","commonSituations":"Redis restart/failover during active chat traffic; local dev without Redis up; brown-out under connection-pool exhaustion. Users mid-conversation suddenly cannot send any message.","solutions":["Retry after ~30 seconds honoring the Retry-After header; the condition is transient.","Ops: restore Redis (check docker compose / redis health); chat streaming depends on it for turn-in-flight tracking.","Client-side: implement exponential backoff capped by Retry-After for 503s on /chat/stream and show a 'service degraded' toast instead of an error dump.","If persistent, verify REDIS connection config and network policy between backend and Redis."],"exampleFix":"// before\nconst res = await fetch('/chat/stream', ...);\nif (!res.ok) throw new Error(res.status); // raw 503\n\n// after — honor Retry-After on 503\nif (res.status === 503) {\n  const wait = Number(res.headers.get('Retry-After') ?? 30) * 1000;\n  await sleep(wait);\n  return retryOnce();\n}","handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"try { await post('/chat/stream', body); } catch (e) {\n  if (e.status === 503) {\n    const wait = Number(e.headers?.['retry-after'] ?? 30) * 1000;\n    await sleep(wait);\n    return retryWithBackoff(postStream, body, {max: 3});\n  }\n  throw e;\n}","preventionTips":["Honor the Retry-After header on 503 instead of immediate retries","Distinguish 503 (infrastructure) from 429 (limit) in chat error UX","Preserve the drafted message locally across retries so nothing is lost"],"tags":["http","redis","availability","retry-after","chat","fastapi"],"backgroundTag":null,"analyzedSha":"9c8bb5550f446ba5d3046b78896578742495b3cf","analyzedAt":"2026-08-14T17:17:21.957Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}