{"product_id":"claude-api-error-handling-retry-rate-limit-va-production-resilience","title":"Claude API Error Handling — Retry, Rate Limit và Production Resilience","description":"\n\u003cp\u003eKhi triển khai Claude API trong môi trường production, lỗi là điều không thể tránh khỏi. Server quá tải, rate limit bị vượt ngưỡng, hoặc mạng gián đoạn tạm thời. Ứng dụng chuyên nghiệp không phải là ứng dụng không bao giờ gặp lỗi, mà là ứng dụng biết cách xử lý lỗi một cách thông minh. Bài viết này hướng dẫn các pattern xử lý lỗi từ cơ bản đến nâng cao cho Claude API.\u003c\/p\u003e\n\n\u003ch2\u003eCác loại lỗi thường gặp\u003c\/h2\u003e\n\u003cp\u003eClaude API trả về các HTTP status code chuẩn. Hiểu rõ từng loại lỗi giúp bạn quyết định chiến lược xử lý phù hợp:\u003c\/p\u003e\n\n\u003ch3\u003eLỗi có thể retry (Retryable errors)\u003c\/h3\u003e\n\u003ctable\u003e\n  \u003cthead\u003e\n    \u003ctr\u003e\n      \u003cth\u003eStatus Code\u003c\/th\u003e\n      \u003cth\u003eTên\u003c\/th\u003e\n      \u003cth\u003eNguyên nhân\u003c\/th\u003e\n      \u003cth\u003eChiến lược\u003c\/th\u003e\n    \u003c\/tr\u003e\n  \u003c\/thead\u003e\n  \u003ctbody\u003e\n    \u003ctr\u003e\n      \u003ctd\u003e429\u003c\/td\u003e\n      \u003ctd\u003eRate Limit Exceeded\u003c\/td\u003e\n      \u003ctd\u003eVượt quá số request cho phép mỗi phút\u003c\/td\u003e\n      \u003ctd\u003eRetry sau thời gian chờ từ header retry-after\u003c\/td\u003e\n    \u003c\/tr\u003e\n    \u003ctr\u003e\n      \u003ctd\u003e500\u003c\/td\u003e\n      \u003ctd\u003eInternal Server Error\u003c\/td\u003e\n      \u003ctd\u003eLỗi phía server Anthropic\u003c\/td\u003e\n      \u003ctd\u003eRetry với exponential backoff\u003c\/td\u003e\n    \u003c\/tr\u003e\n    \u003ctr\u003e\n      \u003ctd\u003e529\u003c\/td\u003e\n      \u003ctd\u003eOverloaded\u003c\/td\u003e\n      \u003ctd\u003eServer đang quá tải\u003c\/td\u003e\n      \u003ctd\u003eRetry với backoff dài hơn\u003c\/td\u003e\n    \u003c\/tr\u003e\n    \u003ctr\u003e\n      \u003ctd\u003e408\u003c\/td\u003e\n      \u003ctd\u003eRequest Timeout\u003c\/td\u003e\n      \u003ctd\u003eRequest quá thời gian xử lý\u003c\/td\u003e\n      \u003ctd\u003eRetry, cân nhắc giảm max_tokens\u003c\/td\u003e\n    \u003c\/tr\u003e\n  \u003c\/tbody\u003e\n\u003c\/table\u003e\n\n\u003ch3\u003eLỗi không nên retry (Non-retryable errors)\u003c\/h3\u003e\n\u003ctable\u003e\n  \u003cthead\u003e\n    \u003ctr\u003e\n      \u003cth\u003eStatus Code\u003c\/th\u003e\n      \u003cth\u003eTên\u003c\/th\u003e\n      \u003cth\u003eNguyên nhân\u003c\/th\u003e\n      \u003cth\u003eHành động\u003c\/th\u003e\n    \u003c\/tr\u003e\n  \u003c\/thead\u003e\n  \u003ctbody\u003e\n    \u003ctr\u003e\n      \u003ctd\u003e400\u003c\/td\u003e\n      \u003ctd\u003eBad Request\u003c\/td\u003e\n      \u003ctd\u003eRequest format sai, thiếu field bắt buộc\u003c\/td\u003e\n      \u003ctd\u003eSửa request, kiểm tra payload\u003c\/td\u003e\n    \u003c\/tr\u003e\n    \u003ctr\u003e\n      \u003ctd\u003e401\u003c\/td\u003e\n      \u003ctd\u003eUnauthorized\u003c\/td\u003e\n      \u003ctd\u003eAPI key không hợp lệ hoặc hết hạn\u003c\/td\u003e\n      \u003ctd\u003eKiểm tra API key\u003c\/td\u003e\n    \u003c\/tr\u003e\n    \u003ctr\u003e\n      \u003ctd\u003e403\u003c\/td\u003e\n      \u003ctd\u003eForbidden\u003c\/td\u003e\n      \u003ctd\u003eKhông có quyền truy cập model hoặc feature\u003c\/td\u003e\n      \u003ctd\u003eKiểm tra quyền tài khoản\u003c\/td\u003e\n    \u003c\/tr\u003e\n    \u003ctr\u003e\n      \u003ctd\u003e404\u003c\/td\u003e\n      \u003ctd\u003eNot Found\u003c\/td\u003e\n      \u003ctd\u003eModel hoặc endpoint không tồn tại\u003c\/td\u003e\n      \u003ctd\u003eKiểm tra model name và URL\u003c\/td\u003e\n    \u003c\/tr\u003e\n  \u003c\/tbody\u003e\n\u003c\/table\u003e\n\n\u003ch2\u003eExponential Backoff với Jitter\u003c\/h2\u003e\n\u003cp\u003eExponential backoff là kỹ thuật tăng thời gian chờ theo cấp số nhân sau mỗi lần retry thất bại. Jitter thêm yếu tố ngẫu nhiên để tránh \"thundering herd\" — hiện tượng nhiều client đồng loạt retry cùng lúc sau khi server phục hồi.\u003c\/p\u003e\n\u003cpre\u003e\u003ccode\u003eimport time\nimport random\nimport anthropic\n\ndef call_with_retry(\n    client,\n    max_retries=5,\n    base_delay=1.0,\n    max_delay=60.0,\n    **kwargs\n):\n    \"\"\"Goi Claude API voi exponential backoff va jitter.\"\"\"\n    last_exception = None\n\n    for attempt in range(max_retries + 1):\n        try:\n            response = client.messages.create(**kwargs)\n            return response\n\n        except anthropic.RateLimitError as e:\n            last_exception = e\n            # Lay retry-after tu header neu co\n            retry_after = getattr(e, \"response\", None)\n            if retry_after and hasattr(retry_after, \"headers\"):\n                wait_time = float(\n                    retry_after.headers.get(\"retry-after\", base_delay)\n                )\n            else:\n                wait_time = base_delay * (2 ** attempt)\n\n            # Them jitter (0.5x den 1.5x)\n            wait_time = wait_time * (0.5 + random.random())\n            wait_time = min(wait_time, max_delay)\n\n            print(f\"Rate limit. Retry sau {wait_time:.1f}s \"\n                  f\"(lan {attempt + 1}\/{max_retries})\")\n            time.sleep(wait_time)\n\n        except anthropic.InternalServerError as e:\n            last_exception = e\n            wait_time = base_delay * (2 ** attempt)\n            wait_time = wait_time * (0.5 + random.random())\n            wait_time = min(wait_time, max_delay)\n\n            print(f\"Server error. Retry sau {wait_time:.1f}s \"\n                  f\"(lan {attempt + 1}\/{max_retries})\")\n            time.sleep(wait_time)\n\n        except anthropic.APIStatusError as e:\n            if e.status_code == 529:\n                last_exception = e\n                wait_time = base_delay * (2 ** attempt) * 2\n                wait_time = wait_time * (0.5 + random.random())\n                wait_time = min(wait_time, max_delay)\n\n                print(f\"Server overloaded. Retry sau {wait_time:.1f}s \"\n                      f\"(lan {attempt + 1}\/{max_retries})\")\n                time.sleep(wait_time)\n            else:\n                # Loi khong the retry (400, 401, 403, 404)\n                raise\n\n        except anthropic.APIConnectionError as e:\n            last_exception = e\n            wait_time = base_delay * (2 ** attempt)\n            wait_time = wait_time * (0.5 + random.random())\n            wait_time = min(wait_time, max_delay)\n\n            print(f\"Connection error. Retry sau {wait_time:.1f}s \"\n                  f\"(lan {attempt + 1}\/{max_retries})\")\n            time.sleep(wait_time)\n\n    raise last_exception\u003c\/code\u003e\u003c\/pre\u003e\n\n\u003ch2\u003eCircuit Breaker Pattern\u003c\/h2\u003e\n\u003cp\u003eCircuit breaker ngăn ứng dụng tiếp tục gửi request khi API đang gặp sự cố nghiêm trọng. Pattern này có ba trạng thái: Closed (hoạt động bình thường), Open (ngắt mạch, từ chối mọi request), và Half-Open (thử gửi một số request để kiểm tra API đã phục hồi chưa).\u003c\/p\u003e\n\u003cpre\u003e\u003ccode\u003eimport time\nfrom enum import Enum\n\nclass CircuitState(Enum):\n    CLOSED = \"closed\"\n    OPEN = \"open\"\n    HALF_OPEN = \"half_open\"\n\nclass CircuitBreaker:\n    def __init__(\n        self,\n        failure_threshold=5,\n        recovery_timeout=60,\n        half_open_max_calls=3\n    ):\n        self.failure_threshold = failure_threshold\n        self.recovery_timeout = recovery_timeout\n        self.half_open_max_calls = half_open_max_calls\n\n        self.state = CircuitState.CLOSED\n        self.failure_count = 0\n        self.last_failure_time = None\n        self.half_open_calls = 0\n\n    def can_execute(self):\n        \"\"\"Kiem tra co nen gui request khong.\"\"\"\n        if self.state == CircuitState.CLOSED:\n            return True\n\n        if self.state == CircuitState.OPEN:\n            # Kiem tra da du thoi gian recovery chua\n            if time.time() - self.last_failure_time \u0026gt;= self.recovery_timeout:\n                self.state = CircuitState.HALF_OPEN\n                self.half_open_calls = 0\n                print(\"Circuit chuyen sang HALF_OPEN\")\n                return True\n            return False\n\n        if self.state == CircuitState.HALF_OPEN:\n            return self.half_open_calls \u0026lt; self.half_open_max_calls\n\n        return False\n\n    def record_success(self):\n        \"\"\"Ghi nhan request thanh cong.\"\"\"\n        if self.state == CircuitState.HALF_OPEN:\n            self.half_open_calls += 1\n            if self.half_open_calls \u0026gt;= self.half_open_max_calls:\n                self.state = CircuitState.CLOSED\n                self.failure_count = 0\n                print(\"Circuit chuyen sang CLOSED (da phuc hoi)\")\n        else:\n            self.failure_count = 0\n\n    def record_failure(self):\n        \"\"\"Ghi nhan request that bai.\"\"\"\n        self.failure_count += 1\n        self.last_failure_time = time.time()\n\n        if self.state == CircuitState.HALF_OPEN:\n            self.state = CircuitState.OPEN\n            print(\"Circuit chuyen sang OPEN (van loi)\")\n        elif self.failure_count \u0026gt;= self.failure_threshold:\n            self.state = CircuitState.OPEN\n            print(f\"Circuit OPEN sau {self.failure_count} loi lien tiep\")\u003c\/code\u003e\u003c\/pre\u003e\n\n\u003ch3\u003eTích hợp Circuit Breaker với API client\u003c\/h3\u003e\n\u003cpre\u003e\u003ccode\u003eclass ResilientClaudeClient:\n    def __init__(self, api_key=None):\n        self.client = anthropic.Anthropic(api_key=api_key)\n        self.circuit = CircuitBreaker(\n            failure_threshold=5,\n            recovery_timeout=60\n        )\n\n    def send_message(self, fallback_response=None, **kwargs):\n        \"\"\"Gui request voi circuit breaker va retry.\"\"\"\n        if not self.circuit.can_execute():\n            print(\"Circuit OPEN - tra ve fallback\")\n            if fallback_response:\n                return fallback_response\n            raise Exception(\"Service dang khong kha dung\")\n\n        try:\n            response = call_with_retry(\n                self.client,\n                max_retries=3,\n                **kwargs\n            )\n            self.circuit.record_success()\n            return response\n\n        except Exception as e:\n            self.circuit.record_failure()\n            if fallback_response:\n                return fallback_response\n            raise\u003c\/code\u003e\u003c\/pre\u003e\n\n\u003ch2\u003eRate Limit Headers\u003c\/h2\u003e\n\u003cp\u003eClaude API trả về các header giúp bạn theo dõi và quản lý rate limit:\u003c\/p\u003e\n\n\u003ctable\u003e\n  \u003cthead\u003e\n    \u003ctr\u003e\n      \u003cth\u003eHeader\u003c\/th\u003e\n      \u003cth\u003eMô tả\u003c\/th\u003e\n    \u003c\/tr\u003e\n  \u003c\/thead\u003e\n  \u003ctbody\u003e\n    \u003ctr\u003e\n      \u003ctd\u003ex-ratelimit-limit-requests\u003c\/td\u003e\n      \u003ctd\u003eSố request tối đa mỗi phút\u003c\/td\u003e\n    \u003c\/tr\u003e\n    \u003ctr\u003e\n      \u003ctd\u003ex-ratelimit-limit-tokens\u003c\/td\u003e\n      \u003ctd\u003eSố token tối đa mỗi phút\u003c\/td\u003e\n    \u003c\/tr\u003e\n    \u003ctr\u003e\n      \u003ctd\u003ex-ratelimit-remaining-requests\u003c\/td\u003e\n      \u003ctd\u003eSố request còn lại trong phút hiện tại\u003c\/td\u003e\n    \u003c\/tr\u003e\n    \u003ctr\u003e\n      \u003ctd\u003ex-ratelimit-remaining-tokens\u003c\/td\u003e\n      \u003ctd\u003eSố token còn lại trong phút hiện tại\u003c\/td\u003e\n    \u003c\/tr\u003e\n    \u003ctr\u003e\n      \u003ctd\u003ex-ratelimit-reset-requests\u003c\/td\u003e\n      \u003ctd\u003eThời điểm reset request limit\u003c\/td\u003e\n    \u003c\/tr\u003e\n    \u003ctr\u003e\n      \u003ctd\u003ex-ratelimit-reset-tokens\u003c\/td\u003e\n      \u003ctd\u003eThời điểm reset token limit\u003c\/td\u003e\n    \u003c\/tr\u003e\n    \u003ctr\u003e\n      \u003ctd\u003eretry-after\u003c\/td\u003e\n      \u003ctd\u003eSố giây cần chờ trước khi retry (khi bị 429)\u003c\/td\u003e\n    \u003c\/tr\u003e\n  \u003c\/tbody\u003e\n\u003c\/table\u003e\n\n\u003ch3\u003eProactive rate limiting\u003c\/h3\u003e\n\u003cp\u003eThay vì chờ bị 429 rồi mới xử lý, bạn có thể chủ động kiểm soát tốc độ gửi request:\u003c\/p\u003e\n\u003cpre\u003e\u003ccode\u003eimport time\nimport threading\n\nclass RateLimiter:\n    def __init__(self, max_requests_per_minute=50, max_tokens_per_minute=40000):\n        self.max_rpm = max_requests_per_minute\n        self.max_tpm = max_tokens_per_minute\n        self.request_timestamps = []\n        self.token_usage = []\n        self.lock = threading.Lock()\n\n    def wait_if_needed(self, estimated_tokens=0):\n        \"\"\"Cho doi neu can thiet de khong vuot rate limit.\"\"\"\n        with self.lock:\n            now = time.time()\n            one_minute_ago = now - 60\n\n            # Xoa cac record cu hon 1 phut\n            self.request_timestamps = [\n                t for t in self.request_timestamps if t \u0026gt; one_minute_ago\n            ]\n            self.token_usage = [\n                (t, tokens) for t, tokens in self.token_usage\n                if t \u0026gt; one_minute_ago\n            ]\n\n            # Kiem tra request limit\n            if len(self.request_timestamps) \u0026gt;= self.max_rpm:\n                wait_time = self.request_timestamps[0] - one_minute_ago\n                print(f\"Request limit. Cho {wait_time:.1f}s\")\n                time.sleep(wait_time + 0.1)\n\n            # Kiem tra token limit\n            total_tokens = sum(t for _, t in self.token_usage)\n            if total_tokens + estimated_tokens \u0026gt; self.max_tpm:\n                wait_time = self.token_usage[0][0] - one_minute_ago\n                print(f\"Token limit. Cho {wait_time:.1f}s\")\n                time.sleep(wait_time + 0.1)\n\n            self.request_timestamps.append(time.time())\n            self.token_usage.append((time.time(), estimated_tokens))\n\n# Su dung\nlimiter = RateLimiter(max_requests_per_minute=50)\n\nfor item in data_to_process:\n    limiter.wait_if_needed(estimated_tokens=500)\n    response = client.messages.create(...)\u003c\/code\u003e\u003c\/pre\u003e\n\n\u003ch2\u003eFallback Strategies\u003c\/h2\u003e\n\u003cp\u003eKhi API liên tục lỗi hoặc circuit breaker mở, ứng dụng cần có phương án dự phòng thay vì chết hoàn toàn:\u003c\/p\u003e\n\n\u003ch3\u003e1. Model Downgrade\u003c\/h3\u003e\n\u003cp\u003eChuyển từ model mạnh hơn xuống model nhẹ hơn khi model chính không khả dụng:\u003c\/p\u003e\n\u003cpre\u003e\u003ccode\u003eMODEL_FALLBACK_CHAIN = [\n    \"claude-sonnet-4-20250514\",\n    \"claude-haiku-3-5-20241022\",\n]\n\ndef call_with_model_fallback(client, messages, **kwargs):\n    \"\"\"Thu tung model theo thu tu uu tien.\"\"\"\n    for model in MODEL_FALLBACK_CHAIN:\n        try:\n            response = client.messages.create(\n                model=model,\n                messages=messages,\n                **kwargs\n            )\n            if model != MODEL_FALLBACK_CHAIN[0]:\n                print(f\"Da fallback sang model: {model}\")\n            return response\n\n        except (anthropic.RateLimitError, anthropic.InternalServerError):\n            print(f\"Model {model} khong kha dung, thu model tiep theo\")\n            continue\n\n    raise Exception(\"Tat ca model deu khong kha dung\")\u003c\/code\u003e\u003c\/pre\u003e\n\n\u003ch3\u003e2. Queue-based Fallback\u003c\/h3\u003e\n\u003cp\u003eĐưa request vào hàng đợi khi API không khả dụng và xử lý sau:\u003c\/p\u003e\n\u003cpre\u003e\u003ccode\u003eimport json\nfrom collections import deque\n\nclass RequestQueue:\n    def __init__(self, max_size=10000):\n        self.queue = deque(maxlen=max_size)\n        self.results = {}\n\n    def enqueue(self, request_id, params):\n        \"\"\"Them request vao hang doi.\"\"\"\n        self.queue.append({\n            \"id\": request_id,\n            \"params\": params,\n            \"enqueued_at\": time.time()\n        })\n        print(f\"Request {request_id} da vao hang doi \"\n              f\"(vi tri {len(self.queue)})\")\n\n    def process_queue(self, client, batch_size=10):\n        \"\"\"Xu ly hang doi khi API kha dung tro lai.\"\"\"\n        processed = 0\n        while self.queue and processed \u0026lt; batch_size:\n            item = self.queue.popleft()\n            try:\n                response = call_with_retry(client, **item[\"params\"])\n                self.results[item[\"id\"]] = {\n                    \"status\": \"success\",\n                    \"response\": response\n                }\n                processed += 1\n            except Exception as e:\n                # Dat lai vao hang doi neu van loi\n                self.queue.appendleft(item)\n                print(f\"Van loi, dung xu ly queue. \"\n                      f\"Con {len(self.queue)} request\")\n                break\n\n        return processed\u003c\/code\u003e\u003c\/pre\u003e\n\n\u003ch2\u003eTriển khai trong Node.js\u003c\/h2\u003e\n\u003cp\u003eTương tự Python, Node.js cần retry logic cho production:\u003c\/p\u003e\n\u003cpre\u003e\u003ccode\u003eimport Anthropic from \"@anthropic-ai\/sdk\";\n\nconst client = new Anthropic();\n\nasync function callWithRetry(params, maxRetries = 5) {\n  let lastError;\n\n  for (let attempt = 0; attempt \u0026lt;= maxRetries; attempt++) {\n    try {\n      return await client.messages.create(params);\n    } catch (error) {\n      lastError = error;\n\n      if (error instanceof Anthropic.RateLimitError) {\n        const retryAfter = error.headers?.[\"retry-after\"];\n        const delay = retryAfter\n          ? parseFloat(retryAfter) * 1000\n          : Math.min(1000 * Math.pow(2, attempt), 60000);\n\n        const jitteredDelay = delay * (0.5 + Math.random());\n        console.log(\n          `Rate limit. Retry sau ${(jitteredDelay \/ 1000).toFixed(1)}s`\n        );\n        await new Promise((r) =\u0026gt; setTimeout(r, jitteredDelay));\n        continue;\n      }\n\n      if (\n        error instanceof Anthropic.InternalServerError ||\n        error.status === 529\n      ) {\n        const delay = Math.min(1000 * Math.pow(2, attempt), 60000);\n        const jitteredDelay = delay * (0.5 + Math.random());\n        await new Promise((r) =\u0026gt; setTimeout(r, jitteredDelay));\n        continue;\n      }\n\n      \/\/ Non-retryable error\n      throw error;\n    }\n  }\n\n  throw lastError;\n}\u003c\/code\u003e\u003c\/pre\u003e\n\n\u003ch2\u003eProduction Error Monitoring\u003c\/h2\u003e\n\u003cp\u003eTrong môi trường production, bạn cần monitoring toàn diện để phát hiện vấn đề sớm:\u003c\/p\u003e\n\u003cpre\u003e\u003ccode\u003eimport logging\nfrom datetime import datetime\n\nlogger = logging.getLogger(\"claude_api\")\n\nclass APIMonitor:\n    def __init__(self, alert_threshold=0.1):\n        self.total_calls = 0\n        self.errors = {429: 0, 500: 0, 529: 0, \"other\": 0}\n        self.latencies = []\n        self.alert_threshold = alert_threshold\n\n    def record_call(self, status_code, latency_ms, model=None):\n        \"\"\"Ghi nhan moi API call.\"\"\"\n        self.total_calls += 1\n        self.latencies.append(latency_ms)\n\n        if status_code \u0026gt;= 400:\n            error_key = status_code if status_code in self.errors else \"other\"\n            self.errors[error_key] = self.errors.get(error_key, 0) + 1\n\n            logger.warning(\n                f\"API error: {status_code} | model: {model} | \"\n                f\"latency: {latency_ms}ms\"\n            )\n\n        # Kiem tra error rate\n        error_total = sum(self.errors.values())\n        error_rate = error_total \/ self.total_calls if self.total_calls \u0026gt; 0 else 0\n\n        if error_rate \u0026gt; self.alert_threshold:\n            self.send_alert(error_rate)\n\n    def send_alert(self, error_rate):\n        \"\"\"Gui canh bao khi error rate vuot nguong.\"\"\"\n        logger.critical(\n            f\"ALERT: Error rate {error_rate:.1%} \"\n            f\"vuot nguong {self.alert_threshold:.1%}! \"\n            f\"Errors: {self.errors}\"\n        )\n        # Tich hop voi Slack, PagerDuty, hoac he thong canh bao khac\n\n    def get_stats(self):\n        \"\"\"Lay thong ke hieu suat.\"\"\"\n        if not self.latencies:\n            return {}\n\n        sorted_lat = sorted(self.latencies)\n        p50 = sorted_lat[len(sorted_lat) \/\/ 2]\n        p95 = sorted_lat[int(len(sorted_lat) * 0.95)]\n        p99 = sorted_lat[int(len(sorted_lat) * 0.99)]\n\n        return {\n            \"total_calls\": self.total_calls,\n            \"error_rate\": sum(self.errors.values()) \/ self.total_calls,\n            \"errors_by_type\": self.errors,\n            \"latency_p50\": f\"{p50}ms\",\n            \"latency_p95\": f\"{p95}ms\",\n            \"latency_p99\": f\"{p99}ms\"\n        }\u003c\/code\u003e\u003c\/pre\u003e\n\n\u003ch2\u003eTimeout và Request Management\u003c\/h2\u003e\n\u003cp\u003eNgoài retry và circuit breaker, timeout là lớp bảo vệ quan trọng khác. Một request không có timeout có thể treo vô thời hạn, chiếm giữ tài nguyên và gây ảnh hưởng đến toàn bộ hệ thống.\u003c\/p\u003e\n\u003cpre\u003e\u003ccode\u003eimport anthropic\n\n# Python SDK ho tro timeout config\nclient = anthropic.Anthropic(\n    timeout=60.0,  # 60 giay cho moi request\n)\n\n# Hoac timeout cho tung request cu the\nimport httpx\n\ndef call_with_timeout(client, timeout=30.0, **kwargs):\n    \"\"\"Goi API voi timeout cu the.\"\"\"\n    try:\n        response = client.messages.create(\n            **kwargs,\n            timeout=timeout\n        )\n        return response\n    except httpx.TimeoutException:\n        print(f\"Request timeout sau {timeout}s\")\n        # Quyet dinh: retry voi timeout dai hon,\n        # hoac fallback sang model nhanh hon\n        return call_with_timeout(\n            client,\n            timeout=timeout * 2,\n            model=\"claude-haiku-3-5-20241022\",  # model nhanh hon\n            **{k: v for k, v in kwargs.items() if k != \"model\"}\n        )\u003c\/code\u003e\u003c\/pre\u003e\n\n\u003ch3\u003eRequest ID tracking\u003c\/h3\u003e\n\u003cp\u003eMỗi response từ Claude API chứa request ID trong header. Lưu lại ID này để debug và liên hệ hỗ trợ khi cần:\u003c\/p\u003e\n\u003cpre\u003e\u003ccode\u003edef call_with_tracking(client, **kwargs):\n    \"\"\"Goi API va luu request ID de debug.\"\"\"\n    try:\n        response = client.messages.create(**kwargs)\n        # Truy cap request ID tu response\n        request_id = response._raw_response.headers.get(\"x-request-id\", \"unknown\")\n        logger.info(f\"Request {request_id}: \"\n                    f\"input={response.usage.input_tokens}, \"\n                    f\"output={response.usage.output_tokens}\")\n        return response\n    except anthropic.APIStatusError as e:\n        request_id = getattr(e.response, \"headers\", {}).get(\n            \"x-request-id\", \"unknown\"\n        )\n        logger.error(f\"Request {request_id} failed: {e.status_code} {e.message}\")\n        raise\u003c\/code\u003e\u003c\/pre\u003e\n\n\u003ch2\u003eStructured Error Response cho end-user\u003c\/h2\u003e\n\u003cp\u003eKhi API lỗi, ứng dụng cần trả về thông báo có ý nghĩa cho người dùng cuối thay vì để lộ chi tiết kỹ thuật:\u003c\/p\u003e\n\u003cpre\u003e\u003ccode\u003eERROR_MESSAGES = {\n    429: {\n        \"user_message\": \"He thong dang xu ly nhieu yeu cau. \"\n                        \"Vui long thu lai sau vai giay.\",\n        \"retry\": True\n    },\n    500: {\n        \"user_message\": \"He thong dang gap su co tam thoi. \"\n                        \"Chung toi dang xu ly, vui long thu lai sau.\",\n        \"retry\": True\n    },\n    529: {\n        \"user_message\": \"He thong dang qua tai. \"\n                        \"Vui long thu lai sau 1-2 phut.\",\n        \"retry\": True\n    },\n    400: {\n        \"user_message\": \"Yeu cau khong hop le. \"\n                        \"Vui long kiem tra lai noi dung.\",\n        \"retry\": False\n    },\n    401: {\n        \"user_message\": \"Phien lam viec da het han. \"\n                        \"Vui long dang nhap lai.\",\n        \"retry\": False\n    }\n}\n\ndef get_user_friendly_error(status_code):\n    \"\"\"Tra ve thong bao loi than thien voi nguoi dung.\"\"\"\n    error_info = ERROR_MESSAGES.get(status_code, {\n        \"user_message\": \"Da xay ra loi. Vui long thu lai sau.\",\n        \"retry\": True\n    })\n    return error_info\u003c\/code\u003e\u003c\/pre\u003e\n\u003cp\u003eNgoài ra, khi sử dụng trong ứng dụng web, bạn nên trả về HTTP status code phù hợp cho frontend. Ví dụ: lỗi 429 từ Claude API có thể trả về 503 (Service Unavailable) cho client kèm header Retry-After, thay vì trả trực tiếp lỗi 429 vì client không gọi Claude API trực tiếp.\u003c\/p\u003e\n\n\u003ch2\u003eChiến lược Graceful Degradation\u003c\/h2\u003e\n\u003cp\u003eỨng dụng tốt vẫn hoạt động được khi AI không khả dụng. Dưới đây là các mức degradation từ nhẹ đến nặng:\u003c\/p\u003e\n\u003cul\u003e\n  \u003cli\u003e\n\u003cstrong\u003eMức 1 - Model downgrade:\u003c\/strong\u003e chuyển từ Sonnet sang Haiku. Chất lượng giảm nhẹ nhưng vẫn có AI response\u003c\/li\u003e\n  \u003cli\u003e\n\u003cstrong\u003eMức 2 - Cached response:\u003c\/strong\u003e trả về response đã cache cho các câu hỏi phổ biến (FAQ). Không cá nhân hóa nhưng vẫn hữu ích\u003c\/li\u003e\n  \u003cli\u003e\n\u003cstrong\u003eMức 3 - Template response:\u003c\/strong\u003e trả về response mẫu dựa trên keyword matching. Không dùng AI nhưng vẫn trả lời được câu hỏi cơ bản\u003c\/li\u003e\n  \u003cli\u003e\n\u003cstrong\u003eMức 4 - Queue và notify:\u003c\/strong\u003e đưa request vào hàng đợi, thông báo cho người dùng sẽ trả lời sau qua email hoặc notification\u003c\/li\u003e\n  \u003cli\u003e\n\u003cstrong\u003eMức 5 - Human handoff:\u003c\/strong\u003e chuyển sang nhân viên hỗ trợ khi tất cả phương án tự động đều thất bại\u003c\/li\u003e\n\u003c\/ul\u003e\n\n\u003ch2\u003eChecklist triển khai production\u003c\/h2\u003e\n\u003cp\u003eTrước khi deploy ứng dụng sử dụng Claude API ra production, hãy kiểm tra danh sách sau:\u003c\/p\u003e\n\u003cul\u003e\n  \u003cli\u003eRetry logic với exponential backoff và jitter cho lỗi 429, 500, 529\u003c\/li\u003e\n  \u003cli\u003eCircuit breaker để ngắt mạch khi API gặp sự cố kéo dài\u003c\/li\u003e\n  \u003cli\u003eRate limiter chủ động để tránh vượt ngưỡng\u003c\/li\u003e\n  \u003cli\u003eModel fallback chain khi model chính không khả dụng\u003c\/li\u003e\n  \u003cli\u003eRequest queue cho các tác vụ không cần phản hồi tức thì\u003c\/li\u003e\n  \u003cli\u003eMonitoring và alerting cho error rate, latency, và chi phí\u003c\/li\u003e\n  \u003cli\u003eLogging đầy đủ với request ID để debug\u003c\/li\u003e\n  \u003cli\u003eTimeout cho mỗi request (không để request treo vô thời hạn)\u003c\/li\u003e\n  \u003cli\u003eGraceful degradation: ứng dụng vẫn hoạt động được dù AI không khả dụng\u003c\/li\u003e\n\u003c\/ul\u003e\n\n\u003ch2\u003eBước tiếp theo\u003c\/h2\u003e\n\u003cp\u003eError handling vững chắc là nền tảng cho mọi ứng dụng production sử dụng Claude API. Kết hợp với Prompt Caching để giảm chi phí và Batch API cho các tác vụ xử lý hàng loạt, bạn sẽ có một hệ thống AI hoàn chỉnh, ổn định và tiết kiệm. Khám phá thêm tại \u003ca href=\"\/collections\/nang-cao\"\u003eThư viện Nâng cao Claude\u003c\/a\u003e.\u003c\/p\u003e\n","brand":"Minh Tuấn","offers":[{"title":"Default Title","offer_id":47730150572244,"sku":null,"price":0.0,"currency_code":"VND","in_stock":true}],"thumbnail_url":"\/\/cdn.shopify.com\/s\/files\/1\/0821\/0264\/9044\/files\/claude-api-error-handling-retry-rate-limit-va-production-resilience.jpg?v=1774715514","url":"https:\/\/claude.vn\/products\/claude-api-error-handling-retry-rate-limit-va-production-resilience","provider":"CLAUDE.VN","version":"1.0","type":"link"}