litellm - ✅(Solved) Fix [Bug]: AzureException APIError - utf-8 codec can't encode character '\ud83e' (surrogates not allowed) causes retry/fallback loop [5 pull requests, 1 participants]

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…
GitHub stats
BerriAI/litellm#23959Fetched 2026-04-08 00:53:43
View on GitHub
Comments
0
Participants
1
Timeline
7
Reactions
0
Participants
Timeline (top)
cross-referenced ×5labeled ×2

Error Message

Alert type: llm_exceptions Level: High Timestamp: 03:47:12

Message: LLM API call failed: litellm.APIError: AzureException APIError - 'utf-8' codec can't encode character '\ud83e' in position 325105: surrogates not allowed. Received Model Group=gpt-5.4 Available Model Group Fallbacks=['gpt-5.4-pro', 'gpt-5.2', 'gpt-5.1', 'gpt-5.2-codex', 'gpt-5.1-codex-max', 'gpt-5-pro', 'gpt-5'] Error doing the fallback: litellm.APIError: AzureException APIError - 'utf-8' codec can't encode character '\ud83e' in position 325103: surrogates not allowedNo fallback model group found for original model_group=gpt-5. Fallbacks=[{'gpt-5.2-codex': ['gpt-5.1-codex-max', 'gpt-5.1', 'gpt-5-pro']}, {'gpt-5.2': ['gpt-5.1', 'gpt-5.1-codex-max', 'gpt-5.2-codex', 'gpt-5', 'gpt-5-pro']}, {'gpt-5.4': ['gpt-5.4-pro', 'gpt-5.2', 'gpt-5.1', 'gpt-5.2-codex', 'gpt-5.1-codex-max', 'gpt-5-pro', 'gpt-5']}]. Received Model Group=gpt-5 Available Model Group Fallbacks=None Error doing the fallback: litellm.APIError: AzureException APIError - 'utf-8' codec can't encode character '\ud83e' in position 325103: surrogates not allowedNo fallback model group found for original model_group=gpt-5. Fallbacks=[{'gpt-5.2-codex': ['gpt-5.1-codex-max', 'gpt-5.1', 'gpt-5-pro']}, {'gpt-5.2': ['gpt-5.1', 'gpt-5.1-codex-max', 'gpt-5.2-codex', 'gpt-5', 'gpt-5-pro']}, {'gpt-5.4': ['gpt-5.4-pro', 'gpt-5.2', 'gpt-5.1', 'gpt-5.2-codex', 'gpt-5.1-codex-max', 'gpt-5-pro', 'gpt-5']}] LiteLLM Retried: 5 times, LiteLLM Max Retries: 5 LiteLLM Retried: 5 times, LiteLLM Max Retries: 5 LiteLLM Retried: 5 times, LiteLLM Max Retries: 5 Model: azure/gpt-5.4 API Base: [redacted Azure endpoint] Messages: None

Proxy URL: [redacted internal proxy URL]

Root Cause

Expected behavior:

  • LiteLLM should sanitize / reject lone surrogate characters before sending the request to the provider
  • or escape the offending value before transport/logging
  • and fallback handling should preserve the original root cause without cascading misleading fallback errors

Fix Action

Fixed

PR fix notes

PR #1: fix: reject malformed Azure surrogate payloads

Description (problem / solution / changelog)

Relevant issues

Fixes BerriAI/litellm#23959

Pre-Submission checklist

Please complete all items before asking a LiteLLM maintainer to review your PR

  • I have Added testing in the tests/test_litellm/ directory, Adding at least 1 test is a hard requirement - see details
  • My PR passes all unit tests on make test-unit
  • My PR's scope is as isolated as possible, it only solves 1 specific problem
  • I have requested a Greptile review by commenting @greptileai and received a Confidence Score of at least 4/5 before requesting a maintainer review

Delays in PR merge?

If you're seeing a delay in your PR being merged, ping the LiteLLM Team on Slack (#pr-review).

CI (LiteLLM team)

CI status guideline:

  • 50-55 passing tests: main is stable with minor issues.
  • 45-49 passing tests: acceptable but needs attention
  • <= 40 passing tests: unstable; be careful with your merges and assess the risk.

Type

🐛 Bug Fix ✅ Test

Changes

  • reject lone surrogate Unicode code points in Azure request payloads before transport dispatch
  • reject escaped lone surrogates at proxy request parsing time with a 400 request_body error
  • add regression coverage for Azure preflight validation, proxy parsing, and router fail-fast retry behavior

Changed files

  • AGENTS.md (modified, +2/-1)
  • ARCHITECTURE.md (modified, +5/-0)
  • docs/agents/README.md (added, +17/-0)
  • docs/coderabbit/review-commands.md (added, +17/-0)
  • docs/engineering/acceptance-criteria.md (added, +35/-0)
  • docs/engineering/harness-engineering.md (added, +28/-0)
  • docs/workflow/one-day-delivery-plan.md (added, +26/-0)
  • docs/workflow/pr-continuity.md (added, +22/-0)
  • litellm/litellm_core_utils/llm_request_utils.py (modified, +37/-1)
  • litellm/llms/azure/azure.py (modified, +34/-3)
  • litellm/llms/azure/common_utils.py (modified, +21/-0)
  • litellm/llms/azure/completion/handler.py (modified, +9/-1)
  • litellm/proxy/common_utils/http_parsing_utils.py (modified, +24/-5)
  • responses.py (added, +145/-0)
  • tests/test_litellm/conftest.py (modified, +36/-42)
  • tests/test_litellm/interactions/test_openapi_compliance.py (modified, +72/-38)
  • tests/test_litellm/llms/azure/test_azure_exception_mapping.py (modified, +86/-79)
  • tests/test_litellm/llms/databricks/responses/__init__.py (removed, +0/-0)
  • tests/test_litellm/llms/manus/responses/__init__.py (removed, +0/-2)
  • tests/test_litellm/llms/openai_like/responses/__init__.py (removed, +0/-0)
  • tests/test_litellm/proxy/client/test_chat.py (modified, +2/-14)
  • tests/test_litellm/proxy/client/test_credentials.py (modified, +0/-7)
  • tests/test_litellm/proxy/client/test_http_client.py (modified, +0/-6)
  • tests/test_litellm/proxy/client/test_http_commands.py (modified, +0/-6)
  • tests/test_litellm/proxy/client/test_keys.py (modified, +2/-13)
  • tests/test_litellm/proxy/client/test_model_groups.py (modified, +0/-7)
  • tests/test_litellm/proxy/client/test_models.py (modified, +5/-25)
  • tests/test_litellm/proxy/common_utils/test_http_parsing_utils.py (modified, +99/-100)
  • tests/test_litellm/test_router_retry_non_retryable_errors.py (modified, +87/-25)

PR #2: fix: reject malformed Azure surrogate payloads

Description (problem / solution / changelog)

Relevant issues

Fixes BerriAI/litellm#23959

Stack

  • base branch: test-google-interactions-openapi-refs
  • issue diff stays isolated to the Azure surrogate validation fix while branch CI unblocks land underneath it in separate PRs

Pre-Submission checklist

Please complete all items before asking a LiteLLM maintainer to review your PR

  • I have Added testing in the tests/test_litellm/ directory, Adding at least 1 test is a hard requirement - see details
  • My PR passes all unit tests on make test-unit
  • My PR's scope is as isolated as possible, it only solves 1 specific problem
  • I have requested a Greptile review by commenting @greptileai and received a Confidence Score of at least 4/5 before requesting a maintainer review

Type

🐛 Bug Fix ✅ Test

Changes

  • reject lone surrogate Unicode code points in Azure request payloads before transport dispatch
  • reject escaped lone surrogates at proxy request parsing time with a 400 request_body error
  • add regression coverage for Azure preflight validation, proxy parsing, and router fail-fast retry behavior

Verification

Changed files

  • litellm/litellm_core_utils/llm_request_utils.py (modified, +37/-1)
  • litellm/llms/azure/azure.py (modified, +34/-3)
  • litellm/llms/azure/common_utils.py (modified, +21/-0)
  • litellm/llms/azure/completion/handler.py (modified, +9/-1)
  • litellm/proxy/common_utils/http_parsing_utils.py (modified, +24/-5)
  • tests/test_litellm/llms/azure/test_azure_exception_mapping.py (modified, +86/-79)
  • tests/test_litellm/proxy/common_utils/test_http_parsing_utils.py (modified, +99/-100)
  • tests/test_litellm/test_router_retry_non_retryable_errors.py (modified, +87/-25)

PR #24380: fix: reject malformed Azure surrogate payloads

Description (problem / solution / changelog)

Relevant issues

Fixes #23959

Pre-Submission checklist

Please complete all items before asking a LiteLLM maintainer to review your PR

  • I have Added testing in the tests/test_litellm/ directory, Adding at least 1 test is a hard requirement - see details
  • My PR passes all unit tests on make test-unit
  • My PR's scope is as isolated as possible, it only solves 1 specific problem
  • I have requested a Greptile review by commenting @greptileai and received a Confidence Score of at least 4/5 before requesting a maintainer review

Delays in PR merge?

If you're seeing a delay in your PR being merged, ping the LiteLLM Team on Slack (#pr-review).

CI (LiteLLM team)

CI status guideline:

  • 50-55 passing tests: main is stable with minor issues.
  • 45-49 passing tests: acceptable but needs attention
  • <= 40 passing tests: unstable; be careful with your merges and assess the risk.

Type

🐛 Bug Fix ✅ Test

Changes

  • reject lone surrogate Unicode code points in Azure request payloads before transport dispatch
  • reject escaped lone surrogates at proxy request parsing time with a 400 request_body error
  • add regression coverage for Azure preflight validation, proxy parsing, and router fail-fast retry behavior

Verification

  • poetry run pytest tests/test_litellm/llms/azure/test_azure_exception_mapping.py tests/test_litellm/proxy/common_utils/test_http_parsing_utils.py tests/test_litellm/test_router_retry_non_retryable_errors.py -v -> 46 passed

Changed files

  • litellm/litellm_core_utils/llm_request_utils.py (modified, +37/-1)
  • litellm/litellm_core_utils/token_counter.py (modified, +6/-3)
  • litellm/llms/azure/azure.py (modified, +34/-3)
  • litellm/llms/azure/common_utils.py (modified, +21/-0)
  • litellm/llms/azure/completion/handler.py (modified, +9/-1)
  • litellm/proxy/common_utils/http_parsing_utils.py (modified, +24/-5)
  • litellm/proxy/management_endpoints/key_management_endpoints.py (modified, +0/-2)
  • responses.py (added, +145/-0)
  • tests/test_litellm/caching/test_redis_cluster_cache.py (modified, +7/-9)
  • tests/test_litellm/conftest.py (modified, +40/-42)
  • tests/test_litellm/integrations/gitlab/test_gitlab_prompt_manager.py (modified, +6/-6)
  • tests/test_litellm/interactions/google_interactions_openapi_snapshot.json (added, +4399/-0)
  • tests/test_litellm/interactions/test_openapi_compliance.py (modified, +75/-60)
  • tests/test_litellm/llms/azure/test_azure_exception_mapping.py (modified, +86/-79)
  • tests/test_litellm/llms/databricks/responses/__init__.py (removed, +0/-0)
  • tests/test_litellm/llms/manus/responses/__init__.py (removed, +0/-2)
  • tests/test_litellm/llms/openai_like/responses/__init__.py (removed, +0/-0)
  • tests/test_litellm/llms/sap/chat/test_sap_chat_calls.py (modified, +11/-6)
  • tests/test_litellm/llms/sap/embed/test_sap_embedding.py (modified, +11/-6)
  • tests/test_litellm/proxy/_experimental/mcp_server/test_mcp_server_manager.py (modified, +101/-143)
  • tests/test_litellm/proxy/client/test_chat.py (modified, +2/-14)
  • tests/test_litellm/proxy/client/test_credentials.py (modified, +0/-7)
  • tests/test_litellm/proxy/client/test_http_client.py (modified, +0/-6)
  • tests/test_litellm/proxy/client/test_http_commands.py (modified, +0/-6)
  • tests/test_litellm/proxy/client/test_keys.py (modified, +2/-13)
  • tests/test_litellm/proxy/client/test_model_groups.py (modified, +0/-7)
  • tests/test_litellm/proxy/client/test_models.py (modified, +5/-25)
  • tests/test_litellm/proxy/common_utils/test_http_parsing_utils.py (modified, +99/-100)
  • tests/test_litellm/proxy/db/test_tool_registry_writer.py (modified, +7/-19)
  • tests/test_litellm/proxy/management_endpoints/test_key_management_endpoints.py (modified, +7/-7)
  • tests/test_litellm/proxy/management_endpoints/test_team_endpoints.py (modified, +3/-0)
  • tests/test_litellm/proxy/management_endpoints/test_ui_sso.py (modified, +441/-486)
  • tests/test_litellm/test_main.py (modified, +48/-70)
  • tests/test_litellm/test_router_retry_non_retryable_errors.py (modified, +87/-25)

PR #16: fix: sanitize malformed Azure surrogate payloads

Description (problem / solution / changelog)

Relevant issues

Fixes BerriAI/litellm#23959

Pre-Submission checklist

Please complete all items before asking a LiteLLM maintainer to review your PR

  • I have Added testing in the tests/test_litellm/ directory, Adding at least 1 test is a hard requirement - see details
  • My PR passes all unit tests on make test-unit
  • My PR's scope is as isolated as possible, it only solves 1 specific problem
  • I have requested a Greptile review by commenting @greptileai and received a Confidence Score of at least 4/5 before requesting a maintainer review

Type

🐛 Bug Fix ✅ Test

Changes

  • sanitize malformed surrogate code points in Azure request payload strings instead of rejecting the request
  • apply the sanitization before Azure SDK/httpx serialization across chat, completion, embedding, assistants, audio transcription, and image request paths
  • keep malformed Unicode transport-safe while preserving valid surrogate pairs
  • keep the fix scoped to Azure request handling instead of broadening proxy-wide request parsing behavior
  • update Azure regression tests to assert sanitize-not-reject behavior

Verification

  • poetry run pytest tests/test_litellm/llms/azure/test_azure_exception_mapping.py tests/test_litellm/test_router_retry_non_retryable_errors.py -v -> 18 passed

Changed files

  • docs/my-website/blog/gpt_5_4_mini_nano/index.md (added, +106/-0)
  • docs/my-website/docs/anthropic_unified/index.md (modified, +6/-3)
  • docs/my-website/docs/completion/prompt_caching.md (modified, +205/-1)
  • docs/my-website/docs/prompt_management.md (added, +48/-0)
  • docs/my-website/docs/providers/gemini.md (modified, +107/-1)
  • docs/my-website/docs/proxy/config_settings.md (modified, +5/-2)
  • docs/my-website/docs/proxy/guardrails/akto.md (added, +139/-0)
  • docs/my-website/docs/proxy/guardrails/custom_guardrail.md (modified, +10/-2)
  • docs/my-website/docs/proxy/high_availability_control_plane.md (added, +190/-0)
  • docs/my-website/docs/proxy/load_balancing.md (modified, +1/-1)
  • docs/my-website/docs/proxy/prompt_management.md (modified, +18/-1)
  • docs/my-website/docs/reasoning_content.md (modified, +85/-2)
  • docs/my-website/docs/response_api.md (modified, +87/-2)
  • docs/my-website/docs/tutorials/claude_code_plugin_marketplace.md (modified, +17/-1)
  • docs/my-website/docs/tutorials/file_search_responses_api.md (added, +241/-0)
  • docs/my-website/docs/tutorials/vertex_ai_pay_go.md (added, +151/-0)
  • docs/my-website/sidebars.js (modified, +4/-0)
  • docs/my-website/src/components/ControlPlaneArchitecture/ControlPlaneArchitecture.tsx (added, +95/-0)
  • docs/my-website/src/components/ControlPlaneArchitecture/index.tsx (added, +1/-0)
  • docs/my-website/src/components/ControlPlaneArchitecture/styles.module.css (added, +517/-0)
  • docs/my-website/static/img/vertex_cost_tracking_flow.svg (added, +63/-0)
  • enterprise/litellm_enterprise/proxy/hooks/managed_files.py (modified, +109/-6)
  • enterprise/pyproject.toml (modified, +2/-2)
  • litellm-proxy-extras/dist/litellm_proxy_extras-0.4.60-py3-none-any.whl (added, +0/-0)
  • litellm-proxy-extras/dist/litellm_proxy_extras-0.4.60.tar.gz (added, +0/-0)
  • litellm-proxy-extras/litellm_proxy_extras/migrations/20260318140652_add_index_to_team_table/migration.sql (modified, +3/-3)
  • litellm-proxy-extras/pyproject.toml (modified, +2/-2)
  • litellm/__init__.py (modified, +1/-0)
  • litellm/_logging.py (modified, +12/-1)
  • litellm/_redis.py (modified, +39/-24)
  • litellm/batches/main.py (modified, +29/-3)
  • litellm/caching/dual_cache.py (modified, +15/-6)
  • litellm/caching/redis_cache.py (modified, +112/-1)
  • litellm/completion_extras/litellm_responses_transformation/transformation.py (modified, +14/-0)
  • litellm/constants.py (modified, +6/-0)
  • litellm/integrations/anthropic_cache_control_hook.py (modified, +8/-1)
  • litellm/integrations/custom_logger.py (modified, +5/-0)
  • litellm/integrations/langsmith.py (modified, +34/-11)
  • litellm/integrations/s3_v2.py (modified, +33/-1)
  • litellm/integrations/websearch_interception/handler.py (modified, +108/-0)
  • litellm/litellm_core_utils/core_helpers.py (modified, +1/-0)
  • litellm/litellm_core_utils/litellm_logging.py (modified, +31/-0)
  • litellm/litellm_core_utils/llm_request_utils.py (modified, +67/-1)
  • litellm/litellm_core_utils/prompt_templates/common_utils.py (modified, +110/-34)
  • litellm/litellm_core_utils/prompt_templates/factory.py (modified, +47/-12)
  • litellm/litellm_core_utils/streaming_handler.py (modified, +44/-12)
  • litellm/llms/anthropic/experimental_pass_through/adapters/handler.py (modified, +22/-9)
  • litellm/llms/anthropic/experimental_pass_through/adapters/transformation.py (modified, +231/-73)
  • litellm/llms/anthropic/experimental_pass_through/messages/handler.py (modified, +83/-1)
  • litellm/llms/anthropic/experimental_pass_through/responses_adapters/transformation.py (modified, +12/-2)
  • litellm/llms/anthropic/experimental_pass_through/utils.py (added, +11/-0)
  • litellm/llms/azure/assistants.py (modified, +11/-3)
  • litellm/llms/azure/audio_transcriptions.py (modified, +11/-3)
  • litellm/llms/azure/azure.py (modified, +34/-3)
  • litellm/llms/azure/chat/gpt_5_transformation.py (modified, +3/-8)
  • litellm/llms/azure/common_utils.py (modified, +10/-0)
  • litellm/llms/azure/completion/handler.py (modified, +9/-1)
  • litellm/llms/azure_ai/agents/handler.py (modified, +88/-18)
  • litellm/llms/base_llm/responses/transformation.py (modified, +8/-0)
  • litellm/llms/bedrock/chat/converse_transformation.py (modified, +10/-0)
  • litellm/llms/bedrock/count_tokens/handler.py (modified, +8/-1)
  • litellm/llms/bedrock/count_tokens/transformation.py (modified, +12/-2)
  • litellm/llms/fireworks_ai/chat/transformation.py (modified, +9/-4)
  • litellm/llms/gemini/image_generation/transformation.py (modified, +7/-4)
  • litellm/llms/mistral/audio_transcription/transformation.py (modified, +7/-0)
  • litellm/llms/mistral/ocr/transformation.py (modified, +4/-0)
  • litellm/llms/moonshot/chat/transformation.py (modified, +7/-3)
  • litellm/llms/openai/chat/gpt_5_transformation.py (modified, +41/-4)
  • litellm/llms/openai/chat/gpt_transformation.py (modified, +3/-2)
  • litellm/llms/openai/responses/transformation.py (modified, +3/-0)
  • litellm/llms/ovhcloud/chat/transformation.py (modified, +6/-4)
  • litellm/llms/vertex_ai/batches/handler.py (modified, +145/-0)
  • litellm/llms/vertex_ai/context_caching/vertex_ai_context_caching.py (modified, +10/-1)
  • litellm/llms/vertex_ai/gemini/transformation.py (modified, +42/-0)
  • litellm/llms/vertex_ai/gemini/vertex_and_google_ai_studio_gemini.py (modified, +138/-26)
  • litellm/llms/vertex_ai/gemini_embeddings/batch_embed_content_transformation.py (modified, +4/-0)
  • litellm/llms/vertex_ai/vertex_ai_partner_models/count_tokens/handler.py (modified, +19/-4)
  • litellm/main.py (modified, +13/-10)
  • litellm/model_prices_and_context_window_backup.json (modified, +173/-35)
  • litellm/proxy/_experimental/out/404.html (renamed, +1/-1)
  • litellm/proxy/_experimental/out/__next.__PAGE__.txt (modified, +22/-21)
  • litellm/proxy/_experimental/out/__next._full.txt (modified, +50/-49)
  • litellm/proxy/_experimental/out/__next._head.txt (modified, +1/-1)
  • litellm/proxy/_experimental/out/__next._index.txt (modified, +4/-4)
  • litellm/proxy/_experimental/out/__next._tree.txt (modified, +2/-2)
  • litellm/proxy/_experimental/out/_next/static/Hp-LQxDEAEt-JSJFExm-i/_buildManifest.js (renamed, +0/-0)
  • litellm/proxy/_experimental/out/_next/static/Hp-LQxDEAEt-JSJFExm-i/_clientMiddlewareManifest.json (renamed, +0/-0)
  • litellm/proxy/_experimental/out/_next/static/Hp-LQxDEAEt-JSJFExm-i/_ssgManifest.js (renamed, +0/-0)
  • litellm/proxy/_experimental/out/_next/static/chunks/02158aed2f4518e2.js (renamed, +1/-1)
  • litellm/proxy/_experimental/out/_next/static/chunks/04a7af91517db55b.js (added, +1/-0)
  • litellm/proxy/_experimental/out/_next/static/chunks/056b4991f668b494.js (removed, +0/-1)
  • litellm/proxy/_experimental/out/_next/static/chunks/065cbe2de8230973.js (added, +1/-0)
  • litellm/proxy/_experimental/out/_next/static/chunks/06ebe9b0e9cdf241.js (removed, +0/-50)
  • litellm/proxy/_experimental/out/_next/static/chunks/0713a1954ae8db53.js (added, +3/-0)
  • litellm/proxy/_experimental/out/_next/static/chunks/08d5ac6e0b6220c0.js (added, +1/-0)
  • litellm/proxy/_experimental/out/_next/static/chunks/0a80887cd471a6cc.js (added, +8/-0)
  • litellm/proxy/_experimental/out/_next/static/chunks/0b8ec8bf90ea9721.js (added, +72/-0)
  • litellm/proxy/_experimental/out/_next/static/chunks/0be054dbc84bd8be.js (renamed, +1/-1)
  • litellm/proxy/_experimental/out/_next/static/chunks/0dda11815be4f78b.js (removed, +0/-105)
  • litellm/proxy/_experimental/out/_next/static/chunks/0ea9112947894f26.js (removed, +0/-1)

PR #24382: fix: sanitize malformed Azure surrogate payloads

Description (problem / solution / changelog)

Relevant issues

Fixes #23959

Pre-Submission checklist

Please complete all items before asking a LiteLLM maintainer to review your PR

  • I have Added testing in the tests/test_litellm/ directory, Adding at least 1 test is a hard requirement - see details
  • My PR passes all unit tests on make test-unit
  • My PR's scope is as isolated as possible, it only solves 1 specific problem
  • I have requested a Greptile review by commenting @greptileai and received a Confidence Score of at least 4/5 before requesting a maintainer review

Type

🐛 Bug Fix ✅ Test

Changes

  • sanitize malformed surrogate code points in Azure request payload strings instead of rejecting the request
  • apply the sanitization before Azure SDK/httpx serialization across chat, completion, embedding, assistants, audio transcription, and image request paths
  • keep malformed Unicode transport-safe while preserving valid surrogate pairs
  • keep the fix scoped to Azure request handling instead of broadening proxy-wide request parsing behavior
  • update Azure regression tests to assert sanitize-not-reject behavior

Verification

  • poetry run pytest tests/test_litellm/llms/azure/test_azure_exception_mapping.py tests/test_litellm/test_router_retry_non_retryable_errors.py -v -> 18 passed

Changed files

  • litellm/litellm_core_utils/llm_request_utils.py (modified, +67/-1)
  • litellm/llms/azure/assistants.py (modified, +11/-3)
  • litellm/llms/azure/audio_transcriptions.py (modified, +11/-3)
  • litellm/llms/azure/azure.py (modified, +34/-3)
  • litellm/llms/azure/common_utils.py (modified, +10/-0)
  • litellm/llms/azure/completion/handler.py (modified, +9/-1)
  • tests/test_litellm/llms/azure/test_azure_exception_mapping.py (modified, +96/-79)

Code Example

Alert type: llm_exceptions
Level: High
Timestamp: 03:47:12

Message: LLM API call failed: `litellm.APIError: AzureException APIError - 'utf-8' codec can't encode character '\ud83e' in position 325105: surrogates not allowed. Received Model Group=gpt-5.4
Available Model Group Fallbacks=['gpt-5.4-pro', 'gpt-5.2', 'gpt-5.1', 'gpt-5.2-codex', 'gpt-5.1-codex-max', 'gpt-5-pro', 'gpt-5']
Error doing the fallback: litellm.APIError: AzureException APIError - 'utf-8' codec can't encode character '\ud83e' in position 325103: surrogates not allowedNo fallback model group found for original model_group=gpt-5. Fallbacks=[{'gpt-5.2-codex': ['gpt-5.1-codex-max', 'gpt-5.1', 'gpt-5-pro']}, {'gpt-5.2': ['gpt-5.1', 'gpt-5.1-codex-max', 'gpt-5.2-codex', 'gpt-5', 'gpt-5-pro']}, {'gpt-5.4': ['gpt-5.4-pro', 'gpt-5.2', 'gpt-5.1', 'gpt-5.2-codex', 'gpt-5.1-codex-max', 'gpt-5-pro', 'gpt-5']}]. Received Model Group=gpt-5
Available Model Group Fallbacks=None
Error doing the fallback: litellm.APIError: AzureException APIError - 'utf-8' codec can't encode character '\ud83e' in position 325103: surrogates not allowedNo fallback model group found for original model_group=gpt-5. Fallbacks=[{'gpt-5.2-codex': ['gpt-5.1-codex-max', 'gpt-5.1', 'gpt-5-pro']}, {'gpt-5.2': ['gpt-5.1', 'gpt-5.1-codex-max', 'gpt-5.2-codex', 'gpt-5', 'gpt-5-pro']}, {'gpt-5.4': ['gpt-5.4-pro', 'gpt-5.2', 'gpt-5.1', 'gpt-5.2-codex', 'gpt-5.1-codex-max', 'gpt-5-pro', 'gpt-5']}] LiteLLM Retried: 5 times, LiteLLM Max Retries: 5 LiteLLM Retried: 5 times, LiteLLM Max Retries: 5 LiteLLM Retried: 5 times, LiteLLM Max Retries: 5
Model: azure/gpt-5.4
API Base: [redacted Azure endpoint]
Messages: None`

Proxy URL: [redacted internal proxy URL]
RAW_BUFFERClick to expand / collapse

Check for existing issues

  • I have searched the existing issues and checked that my issue is not a duplicate.

What happened?

LiteLLM Proxy fails to send a request to Azure with:

litellm.APIError: AzureException APIError - 'utf-8' codec can't encode character '\ud83e' ... surrogates not allowed

This happens while routing azure/gpt-5.4 with configured model-group fallbacks.

Observed behavior:

  • the original request fails with a UnicodeEncodeError on \ud83e
  • LiteLLM retries 5 times
  • fallback attempts also fail with the same encoding error
  • one fallback path additionally emits No fallback model group found for original model_group=gpt-5, which looks like secondary noise after the original malformed input already poisoned the request

Expected behavior:

  • LiteLLM should sanitize / reject lone surrogate characters before sending the request to the provider
  • or escape the offending value before transport/logging
  • and fallback handling should preserve the original root cause without cascading misleading fallback errors

This looks related to malformed Unicode input (likely a broken/truncated emoji surrogate) reaching the Azure request serialization path.

Possibly related to prior surrogate/UTF-8 issues such as #8583, but this case is on Azure + proxy fallback flow.

Steps to Reproduce

  1. Run LiteLLM Proxy with an Azure model group such as gpt-5.4.
  2. Configure fallbacks for that model group.
  3. Send a request where one of the text fields (message content, tool output, or other forwarded text) contains a lone surrogate character such as \ud83e instead of a valid Unicode scalar value.
  4. Observe that the Azure call fails before completion with:
    • 'utf-8' codec can't encode character '\ud83e' ... surrogates not allowed
  5. Observe repeated retries and fallback attempts failing with the same root error.

Relevant log output

Alert type: llm_exceptions
Level: High
Timestamp: 03:47:12

Message: LLM API call failed: `litellm.APIError: AzureException APIError - 'utf-8' codec can't encode character '\ud83e' in position 325105: surrogates not allowed. Received Model Group=gpt-5.4
Available Model Group Fallbacks=['gpt-5.4-pro', 'gpt-5.2', 'gpt-5.1', 'gpt-5.2-codex', 'gpt-5.1-codex-max', 'gpt-5-pro', 'gpt-5']
Error doing the fallback: litellm.APIError: AzureException APIError - 'utf-8' codec can't encode character '\ud83e' in position 325103: surrogates not allowedNo fallback model group found for original model_group=gpt-5. Fallbacks=[{'gpt-5.2-codex': ['gpt-5.1-codex-max', 'gpt-5.1', 'gpt-5-pro']}, {'gpt-5.2': ['gpt-5.1', 'gpt-5.1-codex-max', 'gpt-5.2-codex', 'gpt-5', 'gpt-5-pro']}, {'gpt-5.4': ['gpt-5.4-pro', 'gpt-5.2', 'gpt-5.1', 'gpt-5.2-codex', 'gpt-5.1-codex-max', 'gpt-5-pro', 'gpt-5']}]. Received Model Group=gpt-5
Available Model Group Fallbacks=None
Error doing the fallback: litellm.APIError: AzureException APIError - 'utf-8' codec can't encode character '\ud83e' in position 325103: surrogates not allowedNo fallback model group found for original model_group=gpt-5. Fallbacks=[{'gpt-5.2-codex': ['gpt-5.1-codex-max', 'gpt-5.1', 'gpt-5-pro']}, {'gpt-5.2': ['gpt-5.1', 'gpt-5.1-codex-max', 'gpt-5.2-codex', 'gpt-5', 'gpt-5-pro']}, {'gpt-5.4': ['gpt-5.4-pro', 'gpt-5.2', 'gpt-5.1', 'gpt-5.2-codex', 'gpt-5.1-codex-max', 'gpt-5-pro', 'gpt-5']}] LiteLLM Retried: 5 times, LiteLLM Max Retries: 5 LiteLLM Retried: 5 times, LiteLLM Max Retries: 5 LiteLLM Retried: 5 times, LiteLLM Max Retries: 5
Model: azure/gpt-5.4
API Base: [redacted Azure endpoint]
Messages: None`

Proxy URL: [redacted internal proxy URL]

What part of LiteLLM is this about?

Proxy

What LiteLLM version are you on ?

1.82.3

Twitter / LinkedIn details

No response

extent analysis

Fix Plan

To address the issue of UnicodeEncodeError caused by lone surrogate characters, we need to sanitize the input text before sending it to Azure. We can achieve this by using Python's built-in unicode functions to detect and replace or remove surrogate characters.

Step-by-Step Solution:

  1. Identify and replace surrogate characters: Before sending the request to Azure, iterate through the input text and check for surrogate characters. You can use the following Python function to achieve this:

import re

def remove_surrogate_characters(text): # Use regular expression to find surrogate characters surrogate_pattern = re.compile(r'[\ud800-\udfff]') return surrogate_pattern.sub('', text)

Example usage:

input_text = "Hello, \ud83e world!" cleaned_text = remove_surrogate_characters(input_text) print(cleaned_text) # Output: "Hello, world!"

2. **Integrate the function into the request pipeline**: Modify the LiteLLM Proxy code to call the `remove_surrogate_characters` function before sending the request to Azure. This will ensure that all input text is sanitized before being sent.

#### Code Changes:
You will need to modify the part of the code that handles the request to Azure. The exact changes will depend on the structure of your code, but it should look something like this:
```python
# Assuming 'request_text' is the variable holding the input text
cleaned_request_text = remove_surrogate_characters(request_text)

# Send the cleaned request text to Azure
azure_response = send_request_to_azure(cleaned_request_text)

Verification

To verify that the fix worked, you can test the LiteLLM Proxy with input text containing lone surrogate characters. The proxy should now successfully send the request to Azure without encountering the UnicodeEncodeError.

Extra Tips

  • Make sure to test the remove_surrogate_characters function thoroughly to ensure it correctly handles different types of input text.
  • Consider adding logging to track any instances where surrogate characters are detected and removed, to help with debugging and monitoring.
  • If you're using a library or framework to handle Unicode text, check its documentation for built-in functions or options to handle surrogate characters.

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING