fix(proxy): normalise upstream error.type to DP taxonomy, keep code/param verbatim (#327) - #328

Merged
moonming merged 1 commit into
mainfrom
fix/error-type-normalize-327
May 18, 2026
Merged

fix(proxy): normalise upstream error.type to DP taxonomy, keep code/param verbatim (#327)#328
moonming merged 1 commit into
mainfrom
fix/error-type-normalize-327

Conversation

@moonming

@moonmingmoonming commented May 18, 2026

Copy link
Copy Markdown
Member

Summary

Fixes#327. PR #323 over-corrected by preserving upstream's error.type verbatim — this broke the stable "upstream_error" contract that dp-upstream-error-live.spec.ts:152 already pins, and leaked upstream-private taxonomy (upstream_test_fixture, AccessDeniedException, authentication_error, etc.) to customers.

Contract correction

FieldSourceWhy
error.typeDP taxonomy: "upstream_error"Stable, customer-SDK-actionable, hides upstream zoo
error.messageupstream verbatim (4xx) / canned (5xx)Human-readable, already correct
error.codeupstream verbatim or derived per-wireSDK retry branches on this — preservation from #322 stays
error.paramupstream verbatimTells client which field caused the error — preservation from #322 stays

Customer SDKs branch on error.type == "upstream_error" for upstream-class detection and on error.code for granular retry routing.

Implementation

  • error_translate::render_openai_envelope: hardcodes kind to a new UPSTREAM_ERROR_TYPE constant ("upstream_error").
  • Translation tables collapse from (String, Option<String>) to Option<String> — the per-wire OpenAI type half is now uniformly "upstream_error", so the tables only carry the derived OpenAI string code.
  • 5xx, UpstreamWire::Unknown, and generic-envelope paths already emit "upstream_error" — this commit just makes the 4xx path consistent.

Reference impl divergence (CLAUDE.md §7)

The established gateway impls in this space normalise upstream taxonomy too — LiteLLM maps to a closed set of Python exception classes, Portkey emits its own stable type enum. Choosing a single DP token ("upstream_error") rather than a closed OpenAI-vocabulary set (invalid_request_error, rate_limit_exceeded, server_error) is more conservative — it sidesteps the question of how Bedrock's AccessDeniedException should map onto OpenAI's invalid_request_error vs authentication_error, since the customer just needs "upstream broke; check code for granularity."

Test plan

  • cargo check --workspace --all-targets clean
  • cargo clippy --workspace --all-targets clean
  • cargo fmt --check clean
  • cargo test --workspace — all tests green
  • 22 error_translate::tests updated: body.kind always "upstream_error", code assertions unchanged
  • 3 affected integration tests in aisix-proxy::tests updated and renamed to reflect the corrected contract:
    • upstream_openai_4xx_forwards_code_and_param_but_normalises_type_per_issue_327 (was ..._forwards_full_envelope_per_issue_322)
    • upstream_anthropic_400_normalises_type_to_upstream_error (was ..._passes_through_with_openai_envelope)
    • upstream_anthropic_rate_limit_derives_openai_rate_limit_exceeded_code (was ..._translates_to_openai_rate_limit_exceeded)
  • dp-upstream-error-live.spec.ts:152 should go back to green once :dev image is rebuilt with this PR merged.
  • AISIX-Cloud matrix spec tightening (adapter-openai-overrides-live.spec.ts:634 from upstream_test_fixture to upstream_error) is the issue author's follow-up.

Closes#327

Summary by CodeRabbit

  • Bug Fixes
    • Standardized upstream service error responses to use consistent error type formatting across all provider services, preventing provider-specific error taxonomy from being exposed to client applications.
    • Enhanced error code mapping to improve SDK compatibility and ensure proper retry routing behavior across integrated providers.

Review Change Stack

…/param verbatim (#327)
#323 over-corrected by preserving upstream's `error.type` verbatim,
which:
- broke `dp-upstream-error-live.spec.ts:152` (asserting the stable
`"upstream_error"` token) on every PR's e2e-playwright run from
the merge forward
- leaked upstream-private taxonomy to customers (mock-llm's
`upstream_test_fixture`, Bedrock's `AccessDeniedException`,
Anthropic's `authentication_error` etc.)
- defeated the gateway's role as a normalising layer that hides
the upstream zoo behind a stable, customer-SDK-actionable
taxonomy
This commit restores the DP-stable `error.type = "upstream_error"`
contract for upstream errors, while keeping the #322/#323
preservation of `error.code` and `error.param` — those are still
SDK-actionable and customer-facing-positive.
Final wire contract for `ProxyError::Bridge(UpstreamStatus { .. })`:
| Field | Source |
|----------------|---------------------------------|
| `error.type` | DP taxonomy: `"upstream_error"` |
| `error.message`| upstream verbatim (4xx) / canned `"upstream returned N"` (5xx) |
| `error.code` | upstream verbatim or derived per-wire (Anthropic/Bedrock/Vertex/Azure) |
| `error.param` | upstream verbatim |
Customer SDKs branch on `error.type == "upstream_error"` for
upstream-class detection and on `error.code` for granular retry
routing (`rate_limit_exceeded` vs `insufficient_quota` vs
`model_not_found` etc.).
Implementation:
* `error_translate::render_openai_envelope` hardcodes `kind` to the
new `UPSTREAM_ERROR_TYPE` constant. The translation tables
(Anthropic / Bedrock / Vertex / Azure) collapse from returning
`(String, Option<String>)` to just `Option<String>` — the type
half is now uniformly `"upstream_error"`, so the per-wire tables
only carry the derived OpenAI string code.
* Generic envelope (missing parsed view) also emits
`"upstream_error"` — unchanged from prior behaviour.
* 5xx and `UpstreamWire::Unknown` paths in
`render_bridge_upstream_envelope` already emit `"upstream_error"`;
this commit makes the 4xx path consistent.
Tests:
* All 22 `error_translate::tests` updated: `body.kind` assertions
pinned to `"upstream_error"`; code assertions unchanged.
* 3 integration tests in `aisix-proxy::tests` updated and renamed:
- `upstream_openai_4xx_forwards_full_envelope_per_issue_322` →
`upstream_openai_4xx_forwards_code_and_param_but_normalises_type_per_issue_327`
(asserts `upstream_test_fixture` does NOT leak)
- `upstream_anthropic_400_passes_through_with_openai_envelope` →
`upstream_anthropic_400_normalises_type_to_upstream_error`
- `upstream_anthropic_rate_limit_translates_to_openai_rate_limit_exceeded`
→
`upstream_anthropic_rate_limit_derives_openai_rate_limit_exceeded_code`
(the rate-limit `code` derivation is the customer-facing value;
the `type` is normalised)
Closes#327
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
CopilotAI review requested due to automatic review settings May 18, 2026 05:19
@coderabbitai

coderabbitaiBot commented May 18, 2026

Copy link
Copy Markdown
ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Free

Run ID: 443cdddd-c528-4f52-95ac-29328f6b5e15

📥 Commits

Reviewing files that changed from the base of the PR and between 1a744ee and 0fe40fc.

📒 Files selected for processing (2)
  • crates/aisix-proxy/src/error_translate.rs
  • crates/aisix-proxy/src/lib.rs

📝 Walkthrough

Walkthrough

This PR normalizes upstream error type handling in the proxy: error.type is always set to the DP-stable "upstream_error" token for upstream-originated 4xx errors, while error.code is intelligently derived via provider-specific helpers that map upstream taxonomies to OpenAI-compatible strings, with fallback preservation of upstream codes when derivation is unavailable.

Changes

Error type normalization and code derivation

Layer / File(s)Summary
Documentation and envelope rendering refactoring
crates/aisix-proxy/src/error_translate.rs
Module documentation updated to describe DP-stable error type normalization. The render_openai_envelope function refactored to always set kind to fixed "upstream_error" token and compute derived OpenAI code via per-upstream match logic that prefers derived codes for non-OpenAI wires while preserving upstream codes for OpenAI same-wire.
Error code derivation helpers
crates/aisix-proxy/src/error_translate.rs
Introduced UPSTREAM_ERROR_TYPE constant, updated generic envelope builder, and new derive_anthropic_code, derive_bedrock_code, derive_vertex_code, and derive_azure_code functions returning Option<String> for provider-specific OpenAI-compatible code mappings; Azure falls back to upstream code when derivation returns None.
Anthropic and Bedrock error handling tests
crates/aisix-proxy/src/error_translate.rs
Updated and added unit tests for Anthropic error kinds and Bedrock throttling to assert kind == "upstream_error" while validating derived code values (including None for unmapped kinds).
Bedrock, Vertex, and Azure error mapping tests
crates/aisix-proxy/src/error_translate.rs
Expanded tests for Bedrock quota/validation/access errors, Vertex gRPC status-code mappings, and Azure DeploymentNotFound to verify normalized kind and provider-specific derived code values.
Azure/OpenAI compatibility and same-wire preservation tests
crates/aisix-proxy/src/error_translate.rs
Tests for Azure content-policy violation derivation, Azure OpenAI-compatible code fallback behavior, OpenAI same-wire preservation of both code and param with normalized kind, and missing-parsed-message derived code assertion.
Cross-provider contract tests in lib.rs
crates/aisix-proxy/src/lib.rs
Integration-level tests verifying that OpenAI upstream 4xx and Anthropic 400/rate-limit errors enforce normalized error.type == "upstream_error" at the proxy boundary while preserving granular error.code and error.param for SDK retry branching and Anthropic→OpenAI code derivation.

🎯 3 (Moderate) | ⏱️ ~25 minutes


Note

🎁 Summarized by CodeRabbit Free

Your organization has reached its limit of developer seats under the Pro Plan. For new users, CodeRabbit will generate a high-level summary and a walkthrough for each pull request. For a comprehensive line-by-line review, please add seats to your subscription by visiting https://app.coderabbit.ai/login.If you believe this is a mistake and have available seats, please assign one to the pull request author through the subscription management page using the link above.

Comment @coderabbitai help to get the list of available commands and usage tips.

CopilotAI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR corrects an over-correction from #323: while error.code and error.param are still preserved verbatim from upstream (preserving #322's SDK retry-routing fix), error.type is now normalized to the DP-stable "upstream_error" token across all wires/paths, so upstream-private taxonomies (upstream_test_fixture, AccessDeniedException, authentication_error, etc.) no longer leak to customers. The translation tables in error_translate.rs are collapsed from (type, code) pairs to a single derived code.

Changes:

  • Hardcode kind = "upstream_error" in render_openai_envelope; introduce UPSTREAM_ERROR_TYPE constant.
  • Rename translate_*derive_*_code and return Option<String> only (the OpenAI-shape code).
  • Update unit and integration tests + their docstrings/names to assert the corrected contract.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated no comments.

FileDescription
crates/aisix-proxy/src/error_translate.rsDrop per-wire type derivation; emit DP-stable upstream_error and only derive OpenAI code. Update module doc and tests.
crates/aisix-proxy/src/lib.rsRename/retighten 3 integration tests to assert type == "upstream_error" while keeping code/param preservation assertions.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@moonming

moonming commented May 18, 2026

Copy link
Copy Markdown
MemberAuthor

Audit completed — verdict: merge, no HIGH/MEDIUM findings.

Two LOW findings:

Per CLAUDE.md §8 merge gate: ready when CI passes.

@moonming

Copy link
Copy Markdown
MemberAuthor

Follow-up on the audit's LOW-1: traced the path, not a real bug. BridgeError::UpstreamStatus is only constructed pre-stream (via map_http_error), and the stream itself only yields Transport/UpstreamDecode whose Display impls don't carry upstream body content. Closed #329 with the trace.

@moonming
moonming merged commit 30f70a2 into mainMay 18, 2026
12 checks passed
@moonming
moonming deleted the fix/error-type-normalize-327 branch May 18, 2026 05:30
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

bug(proxy): #322 over-corrected — error.type should normalize to DP taxonomy, not verbatim from upstream

2 participants

@moonming
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

fix(proxy): normalise upstream error.type to DP taxonomy, keep code/param verbatim (#327) - #328

Merged
moonming merged 1 commit into
mainfrom
fix/error-type-normalize-327
May 18, 2026
Merged

fix(proxy): normalise upstream error.type to DP taxonomy, keep code/param verbatim (#327)#328
moonming merged 1 commit into
mainfrom
fix/error-type-normalize-327

Conversation

@moonming

@moonmingmoonming commented May 18, 2026

Copy link
Copy Markdown
Member

Summary

Fixes#327. PR #323 over-corrected by preserving upstream's error.type verbatim — this broke the stable "upstream_error" contract that dp-upstream-error-live.spec.ts:152 already pins, and leaked upstream-private taxonomy (upstream_test_fixture, AccessDeniedException, authentication_error, etc.) to customers.

Contract correction

FieldSourceWhy
error.typeDP taxonomy: "upstream_error"Stable, customer-SDK-actionable, hides upstream zoo
error.messageupstream verbatim (4xx) / canned (5xx)Human-readable, already correct
error.codeupstream verbatim or derived per-wireSDK retry branches on this — preservation from #322 stays
error.paramupstream verbatimTells client which field caused the error — preservation from #322 stays

Customer SDKs branch on error.type == "upstream_error" for upstream-class detection and on error.code for granular retry routing.

Implementation

  • error_translate::render_openai_envelope: hardcodes kind to a new UPSTREAM_ERROR_TYPE constant ("upstream_error").
  • Translation tables collapse from (String, Option<String>) to Option<String> — the per-wire OpenAI type half is now uniformly "upstream_error", so the tables only carry the derived OpenAI string code.
  • 5xx, UpstreamWire::Unknown, and generic-envelope paths already emit "upstream_error" — this commit just makes the 4xx path consistent.

Reference impl divergence (CLAUDE.md §7)

The established gateway impls in this space normalise upstream taxonomy too — LiteLLM maps to a closed set of Python exception classes, Portkey emits its own stable type enum. Choosing a single DP token ("upstream_error") rather than a closed OpenAI-vocabulary set (invalid_request_error, rate_limit_exceeded, server_error) is more conservative — it sidesteps the question of how Bedrock's AccessDeniedException should map onto OpenAI's invalid_request_error vs authentication_error, since the customer just needs "upstream broke; check code for granularity."

Test plan

  • cargo check --workspace --all-targets clean
  • cargo clippy --workspace --all-targets clean
  • cargo fmt --check clean
  • cargo test --workspace — all tests green
  • 22 error_translate::tests updated: body.kind always "upstream_error", code assertions unchanged
  • 3 affected integration tests in aisix-proxy::tests updated and renamed to reflect the corrected contract:
    • upstream_openai_4xx_forwards_code_and_param_but_normalises_type_per_issue_327 (was ..._forwards_full_envelope_per_issue_322)
    • upstream_anthropic_400_normalises_type_to_upstream_error (was ..._passes_through_with_openai_envelope)
    • upstream_anthropic_rate_limit_derives_openai_rate_limit_exceeded_code (was ..._translates_to_openai_rate_limit_exceeded)
  • dp-upstream-error-live.spec.ts:152 should go back to green once :dev image is rebuilt with this PR merged.
  • AISIX-Cloud matrix spec tightening (adapter-openai-overrides-live.spec.ts:634 from upstream_test_fixture to upstream_error) is the issue author's follow-up.

Closes#327

Summary by CodeRabbit

  • Bug Fixes
    • Standardized upstream service error responses to use consistent error type formatting across all provider services, preventing provider-specific error taxonomy from being exposed to client applications.
    • Enhanced error code mapping to improve SDK compatibility and ensure proper retry routing behavior across integrated providers.

Review Change Stack

…/param verbatim (#327)
#323 over-corrected by preserving upstream's `error.type` verbatim,
which:
- broke `dp-upstream-error-live.spec.ts:152` (asserting the stable
`"upstream_error"` token) on every PR's e2e-playwright run from
the merge forward
- leaked upstream-private taxonomy to customers (mock-llm's
`upstream_test_fixture`, Bedrock's `AccessDeniedException`,
Anthropic's `authentication_error` etc.)
- defeated the gateway's role as a normalising layer that hides
the upstream zoo behind a stable, customer-SDK-actionable
taxonomy
This commit restores the DP-stable `error.type = "upstream_error"`
contract for upstream errors, while keeping the #322/#323
preservation of `error.code` and `error.param` — those are still
SDK-actionable and customer-facing-positive.
Final wire contract for `ProxyError::Bridge(UpstreamStatus { .. })`:
| Field | Source |
|----------------|---------------------------------|
| `error.type` | DP taxonomy: `"upstream_error"` |
| `error.message`| upstream verbatim (4xx) / canned `"upstream returned N"` (5xx) |
| `error.code` | upstream verbatim or derived per-wire (Anthropic/Bedrock/Vertex/Azure) |
| `error.param` | upstream verbatim |
Customer SDKs branch on `error.type == "upstream_error"` for
upstream-class detection and on `error.code` for granular retry
routing (`rate_limit_exceeded` vs `insufficient_quota` vs
`model_not_found` etc.).
Implementation:
* `error_translate::render_openai_envelope` hardcodes `kind` to the
new `UPSTREAM_ERROR_TYPE` constant. The translation tables
(Anthropic / Bedrock / Vertex / Azure) collapse from returning
`(String, Option<String>)` to just `Option<String>` — the type
half is now uniformly `"upstream_error"`, so the per-wire tables
only carry the derived OpenAI string code.
* Generic envelope (missing parsed view) also emits
`"upstream_error"` — unchanged from prior behaviour.
* 5xx and `UpstreamWire::Unknown` paths in
`render_bridge_upstream_envelope` already emit `"upstream_error"`;
this commit makes the 4xx path consistent.
Tests:
* All 22 `error_translate::tests` updated: `body.kind` assertions
pinned to `"upstream_error"`; code assertions unchanged.
* 3 integration tests in `aisix-proxy::tests` updated and renamed:
- `upstream_openai_4xx_forwards_full_envelope_per_issue_322` →
`upstream_openai_4xx_forwards_code_and_param_but_normalises_type_per_issue_327`
(asserts `upstream_test_fixture` does NOT leak)
- `upstream_anthropic_400_passes_through_with_openai_envelope` →
`upstream_anthropic_400_normalises_type_to_upstream_error`
- `upstream_anthropic_rate_limit_translates_to_openai_rate_limit_exceeded`
→
`upstream_anthropic_rate_limit_derives_openai_rate_limit_exceeded_code`
(the rate-limit `code` derivation is the customer-facing value;
the `type` is normalised)
Closes#327
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
CopilotAI review requested due to automatic review settings May 18, 2026 05:19
@coderabbitai

coderabbitaiBot commented May 18, 2026

Copy link
Copy Markdown
ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Free

Run ID: 443cdddd-c528-4f52-95ac-29328f6b5e15

📥 Commits

Reviewing files that changed from the base of the PR and between 1a744ee and 0fe40fc.

📒 Files selected for processing (2)
  • crates/aisix-proxy/src/error_translate.rs
  • crates/aisix-proxy/src/lib.rs

📝 Walkthrough

Walkthrough

This PR normalizes upstream error type handling in the proxy: error.type is always set to the DP-stable "upstream_error" token for upstream-originated 4xx errors, while error.code is intelligently derived via provider-specific helpers that map upstream taxonomies to OpenAI-compatible strings, with fallback preservation of upstream codes when derivation is unavailable.

Changes

Error type normalization and code derivation

Layer / File(s)Summary
Documentation and envelope rendering refactoring
crates/aisix-proxy/src/error_translate.rs
Module documentation updated to describe DP-stable error type normalization. The render_openai_envelope function refactored to always set kind to fixed "upstream_error" token and compute derived OpenAI code via per-upstream match logic that prefers derived codes for non-OpenAI wires while preserving upstream codes for OpenAI same-wire.
Error code derivation helpers
crates/aisix-proxy/src/error_translate.rs
Introduced UPSTREAM_ERROR_TYPE constant, updated generic envelope builder, and new derive_anthropic_code, derive_bedrock_code, derive_vertex_code, and derive_azure_code functions returning Option<String> for provider-specific OpenAI-compatible code mappings; Azure falls back to upstream code when derivation returns None.
Anthropic and Bedrock error handling tests
crates/aisix-proxy/src/error_translate.rs
Updated and added unit tests for Anthropic error kinds and Bedrock throttling to assert kind == "upstream_error" while validating derived code values (including None for unmapped kinds).
Bedrock, Vertex, and Azure error mapping tests
crates/aisix-proxy/src/error_translate.rs
Expanded tests for Bedrock quota/validation/access errors, Vertex gRPC status-code mappings, and Azure DeploymentNotFound to verify normalized kind and provider-specific derived code values.
Azure/OpenAI compatibility and same-wire preservation tests
crates/aisix-proxy/src/error_translate.rs
Tests for Azure content-policy violation derivation, Azure OpenAI-compatible code fallback behavior, OpenAI same-wire preservation of both code and param with normalized kind, and missing-parsed-message derived code assertion.
Cross-provider contract tests in lib.rs
crates/aisix-proxy/src/lib.rs
Integration-level tests verifying that OpenAI upstream 4xx and Anthropic 400/rate-limit errors enforce normalized error.type == "upstream_error" at the proxy boundary while preserving granular error.code and error.param for SDK retry branching and Anthropic→OpenAI code derivation.

🎯 3 (Moderate) | ⏱️ ~25 minutes


Note

🎁 Summarized by CodeRabbit Free

Your organization has reached its limit of developer seats under the Pro Plan. For new users, CodeRabbit will generate a high-level summary and a walkthrough for each pull request. For a comprehensive line-by-line review, please add seats to your subscription by visiting https://app.coderabbit.ai/login.If you believe this is a mistake and have available seats, please assign one to the pull request author through the subscription management page using the link above.

Comment @coderabbitai help to get the list of available commands and usage tips.

CopilotAI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR corrects an over-correction from #323: while error.code and error.param are still preserved verbatim from upstream (preserving #322's SDK retry-routing fix), error.type is now normalized to the DP-stable "upstream_error" token across all wires/paths, so upstream-private taxonomies (upstream_test_fixture, AccessDeniedException, authentication_error, etc.) no longer leak to customers. The translation tables in error_translate.rs are collapsed from (type, code) pairs to a single derived code.

Changes:

  • Hardcode kind = "upstream_error" in render_openai_envelope; introduce UPSTREAM_ERROR_TYPE constant.
  • Rename translate_*derive_*_code and return Option<String> only (the OpenAI-shape code).
  • Update unit and integration tests + their docstrings/names to assert the corrected contract.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated no comments.

FileDescription
crates/aisix-proxy/src/error_translate.rsDrop per-wire type derivation; emit DP-stable upstream_error and only derive OpenAI code. Update module doc and tests.
crates/aisix-proxy/src/lib.rsRename/retighten 3 integration tests to assert type == "upstream_error" while keeping code/param preservation assertions.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@moonming

moonming commented May 18, 2026

Copy link
Copy Markdown
MemberAuthor

Audit completed — verdict: merge, no HIGH/MEDIUM findings.

Two LOW findings:

Per CLAUDE.md §8 merge gate: ready when CI passes.

@moonming

Copy link
Copy Markdown
MemberAuthor

Follow-up on the audit's LOW-1: traced the path, not a real bug. BridgeError::UpstreamStatus is only constructed pre-stream (via map_http_error), and the stream itself only yields Transport/UpstreamDecode whose Display impls don't carry upstream body content. Closed #329 with the trace.

@moonming
moonming merged commit 30f70a2 into mainMay 18, 2026
12 checks passed
@moonming
moonming deleted the fix/error-type-normalize-327 branch May 18, 2026 05:30
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

bug(proxy): #322 over-corrected — error.type should normalize to DP taxonomy, not verbatim from upstream

2 participants

@moonming
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

fix(proxy): normalise upstream error.type to DP taxonomy, keep code/param verbatim (#327) - #328

Merged
moonming merged 1 commit into
mainfrom
fix/error-type-normalize-327
May 18, 2026
Merged

fix(proxy): normalise upstream error.type to DP taxonomy, keep code/param verbatim (#327)#328
moonming merged 1 commit into
mainfrom
fix/error-type-normalize-327

Conversation

@moonming

@moonmingmoonming commented May 18, 2026

Copy link
Copy Markdown
Member

Summary

Fixes#327. PR #323 over-corrected by preserving upstream's error.type verbatim — this broke the stable "upstream_error" contract that dp-upstream-error-live.spec.ts:152 already pins, and leaked upstream-private taxonomy (upstream_test_fixture, AccessDeniedException, authentication_error, etc.) to customers.

Contract correction

FieldSourceWhy
error.typeDP taxonomy: "upstream_error"Stable, customer-SDK-actionable, hides upstream zoo
error.messageupstream verbatim (4xx) / canned (5xx)Human-readable, already correct
error.codeupstream verbatim or derived per-wireSDK retry branches on this — preservation from #322 stays
error.paramupstream verbatimTells client which field caused the error — preservation from #322 stays

Customer SDKs branch on error.type == "upstream_error" for upstream-class detection and on error.code for granular retry routing.

Implementation

  • error_translate::render_openai_envelope: hardcodes kind to a new UPSTREAM_ERROR_TYPE constant ("upstream_error").
  • Translation tables collapse from (String, Option<String>) to Option<String> — the per-wire OpenAI type half is now uniformly "upstream_error", so the tables only carry the derived OpenAI string code.
  • 5xx, UpstreamWire::Unknown, and generic-envelope paths already emit "upstream_error" — this commit just makes the 4xx path consistent.

Reference impl divergence (CLAUDE.md §7)

The established gateway impls in this space normalise upstream taxonomy too — LiteLLM maps to a closed set of Python exception classes, Portkey emits its own stable type enum. Choosing a single DP token ("upstream_error") rather than a closed OpenAI-vocabulary set (invalid_request_error, rate_limit_exceeded, server_error) is more conservative — it sidesteps the question of how Bedrock's AccessDeniedException should map onto OpenAI's invalid_request_error vs authentication_error, since the customer just needs "upstream broke; check code for granularity."

Test plan

  • cargo check --workspace --all-targets clean
  • cargo clippy --workspace --all-targets clean
  • cargo fmt --check clean
  • cargo test --workspace — all tests green
  • 22 error_translate::tests updated: body.kind always "upstream_error", code assertions unchanged
  • 3 affected integration tests in aisix-proxy::tests updated and renamed to reflect the corrected contract:
    • upstream_openai_4xx_forwards_code_and_param_but_normalises_type_per_issue_327 (was ..._forwards_full_envelope_per_issue_322)
    • upstream_anthropic_400_normalises_type_to_upstream_error (was ..._passes_through_with_openai_envelope)
    • upstream_anthropic_rate_limit_derives_openai_rate_limit_exceeded_code (was ..._translates_to_openai_rate_limit_exceeded)
  • dp-upstream-error-live.spec.ts:152 should go back to green once :dev image is rebuilt with this PR merged.
  • AISIX-Cloud matrix spec tightening (adapter-openai-overrides-live.spec.ts:634 from upstream_test_fixture to upstream_error) is the issue author's follow-up.

Closes#327

Summary by CodeRabbit

  • Bug Fixes
    • Standardized upstream service error responses to use consistent error type formatting across all provider services, preventing provider-specific error taxonomy from being exposed to client applications.
    • Enhanced error code mapping to improve SDK compatibility and ensure proper retry routing behavior across integrated providers.

Review Change Stack

…/param verbatim (#327)
#323 over-corrected by preserving upstream's `error.type` verbatim,
which:
- broke `dp-upstream-error-live.spec.ts:152` (asserting the stable
`"upstream_error"` token) on every PR's e2e-playwright run from
the merge forward
- leaked upstream-private taxonomy to customers (mock-llm's
`upstream_test_fixture`, Bedrock's `AccessDeniedException`,
Anthropic's `authentication_error` etc.)
- defeated the gateway's role as a normalising layer that hides
the upstream zoo behind a stable, customer-SDK-actionable
taxonomy
This commit restores the DP-stable `error.type = "upstream_error"`
contract for upstream errors, while keeping the #322/#323
preservation of `error.code` and `error.param` — those are still
SDK-actionable and customer-facing-positive.
Final wire contract for `ProxyError::Bridge(UpstreamStatus { .. })`:
| Field | Source |
|----------------|---------------------------------|
| `error.type` | DP taxonomy: `"upstream_error"` |
| `error.message`| upstream verbatim (4xx) / canned `"upstream returned N"` (5xx) |
| `error.code` | upstream verbatim or derived per-wire (Anthropic/Bedrock/Vertex/Azure) |
| `error.param` | upstream verbatim |
Customer SDKs branch on `error.type == "upstream_error"` for
upstream-class detection and on `error.code` for granular retry
routing (`rate_limit_exceeded` vs `insufficient_quota` vs
`model_not_found` etc.).
Implementation:
* `error_translate::render_openai_envelope` hardcodes `kind` to the
new `UPSTREAM_ERROR_TYPE` constant. The translation tables
(Anthropic / Bedrock / Vertex / Azure) collapse from returning
`(String, Option<String>)` to just `Option<String>` — the type
half is now uniformly `"upstream_error"`, so the per-wire tables
only carry the derived OpenAI string code.
* Generic envelope (missing parsed view) also emits
`"upstream_error"` — unchanged from prior behaviour.
* 5xx and `UpstreamWire::Unknown` paths in
`render_bridge_upstream_envelope` already emit `"upstream_error"`;
this commit makes the 4xx path consistent.
Tests:
* All 22 `error_translate::tests` updated: `body.kind` assertions
pinned to `"upstream_error"`; code assertions unchanged.
* 3 integration tests in `aisix-proxy::tests` updated and renamed:
- `upstream_openai_4xx_forwards_full_envelope_per_issue_322` →
`upstream_openai_4xx_forwards_code_and_param_but_normalises_type_per_issue_327`
(asserts `upstream_test_fixture` does NOT leak)
- `upstream_anthropic_400_passes_through_with_openai_envelope` →
`upstream_anthropic_400_normalises_type_to_upstream_error`
- `upstream_anthropic_rate_limit_translates_to_openai_rate_limit_exceeded`
→
`upstream_anthropic_rate_limit_derives_openai_rate_limit_exceeded_code`
(the rate-limit `code` derivation is the customer-facing value;
the `type` is normalised)
Closes#327
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
CopilotAI review requested due to automatic review settings May 18, 2026 05:19
@coderabbitai

coderabbitaiBot commented May 18, 2026

Copy link
Copy Markdown
ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Free

Run ID: 443cdddd-c528-4f52-95ac-29328f6b5e15

📥 Commits

Reviewing files that changed from the base of the PR and between 1a744ee and 0fe40fc.

📒 Files selected for processing (2)
  • crates/aisix-proxy/src/error_translate.rs
  • crates/aisix-proxy/src/lib.rs

📝 Walkthrough

Walkthrough

This PR normalizes upstream error type handling in the proxy: error.type is always set to the DP-stable "upstream_error" token for upstream-originated 4xx errors, while error.code is intelligently derived via provider-specific helpers that map upstream taxonomies to OpenAI-compatible strings, with fallback preservation of upstream codes when derivation is unavailable.

Changes

Error type normalization and code derivation

Layer / File(s)Summary
Documentation and envelope rendering refactoring
crates/aisix-proxy/src/error_translate.rs
Module documentation updated to describe DP-stable error type normalization. The render_openai_envelope function refactored to always set kind to fixed "upstream_error" token and compute derived OpenAI code via per-upstream match logic that prefers derived codes for non-OpenAI wires while preserving upstream codes for OpenAI same-wire.
Error code derivation helpers
crates/aisix-proxy/src/error_translate.rs
Introduced UPSTREAM_ERROR_TYPE constant, updated generic envelope builder, and new derive_anthropic_code, derive_bedrock_code, derive_vertex_code, and derive_azure_code functions returning Option<String> for provider-specific OpenAI-compatible code mappings; Azure falls back to upstream code when derivation returns None.
Anthropic and Bedrock error handling tests
crates/aisix-proxy/src/error_translate.rs
Updated and added unit tests for Anthropic error kinds and Bedrock throttling to assert kind == "upstream_error" while validating derived code values (including None for unmapped kinds).
Bedrock, Vertex, and Azure error mapping tests
crates/aisix-proxy/src/error_translate.rs
Expanded tests for Bedrock quota/validation/access errors, Vertex gRPC status-code mappings, and Azure DeploymentNotFound to verify normalized kind and provider-specific derived code values.
Azure/OpenAI compatibility and same-wire preservation tests
crates/aisix-proxy/src/error_translate.rs
Tests for Azure content-policy violation derivation, Azure OpenAI-compatible code fallback behavior, OpenAI same-wire preservation of both code and param with normalized kind, and missing-parsed-message derived code assertion.
Cross-provider contract tests in lib.rs
crates/aisix-proxy/src/lib.rs
Integration-level tests verifying that OpenAI upstream 4xx and Anthropic 400/rate-limit errors enforce normalized error.type == "upstream_error" at the proxy boundary while preserving granular error.code and error.param for SDK retry branching and Anthropic→OpenAI code derivation.

🎯 3 (Moderate) | ⏱️ ~25 minutes


Note

🎁 Summarized by CodeRabbit Free

Your organization has reached its limit of developer seats under the Pro Plan. For new users, CodeRabbit will generate a high-level summary and a walkthrough for each pull request. For a comprehensive line-by-line review, please add seats to your subscription by visiting https://app.coderabbit.ai/login.If you believe this is a mistake and have available seats, please assign one to the pull request author through the subscription management page using the link above.

Comment @coderabbitai help to get the list of available commands and usage tips.

CopilotAI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR corrects an over-correction from #323: while error.code and error.param are still preserved verbatim from upstream (preserving #322's SDK retry-routing fix), error.type is now normalized to the DP-stable "upstream_error" token across all wires/paths, so upstream-private taxonomies (upstream_test_fixture, AccessDeniedException, authentication_error, etc.) no longer leak to customers. The translation tables in error_translate.rs are collapsed from (type, code) pairs to a single derived code.

Changes:

  • Hardcode kind = "upstream_error" in render_openai_envelope; introduce UPSTREAM_ERROR_TYPE constant.
  • Rename translate_*derive_*_code and return Option<String> only (the OpenAI-shape code).
  • Update unit and integration tests + their docstrings/names to assert the corrected contract.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated no comments.

FileDescription
crates/aisix-proxy/src/error_translate.rsDrop per-wire type derivation; emit DP-stable upstream_error and only derive OpenAI code. Update module doc and tests.
crates/aisix-proxy/src/lib.rsRename/retighten 3 integration tests to assert type == "upstream_error" while keeping code/param preservation assertions.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@moonming

moonming commented May 18, 2026

Copy link
Copy Markdown
MemberAuthor

Audit completed — verdict: merge, no HIGH/MEDIUM findings.

Two LOW findings:

Per CLAUDE.md §8 merge gate: ready when CI passes.

@moonming

Copy link
Copy Markdown
MemberAuthor

Follow-up on the audit's LOW-1: traced the path, not a real bug. BridgeError::UpstreamStatus is only constructed pre-stream (via map_http_error), and the stream itself only yields Transport/UpstreamDecode whose Display impls don't carry upstream body content. Closed #329 with the trace.

@moonming
moonming merged commit 30f70a2 into mainMay 18, 2026
12 checks passed
@moonming
moonming deleted the fix/error-type-normalize-327 branch May 18, 2026 05:30
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

bug(proxy): #322 over-corrected — error.type should normalize to DP taxonomy, not verbatim from upstream

2 participants

@moonming
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

fix(proxy): normalise upstream error.type to DP taxonomy, keep code/param verbatim (#327) - #328

Merged
moonming merged 1 commit into
mainfrom
fix/error-type-normalize-327
May 18, 2026
Merged

fix(proxy): normalise upstream error.type to DP taxonomy, keep code/param verbatim (#327)#328
moonming merged 1 commit into
mainfrom
fix/error-type-normalize-327

Conversation

@moonming

@moonmingmoonming commented May 18, 2026

Copy link
Copy Markdown
Member

Summary

Fixes#327. PR #323 over-corrected by preserving upstream's error.type verbatim — this broke the stable "upstream_error" contract that dp-upstream-error-live.spec.ts:152 already pins, and leaked upstream-private taxonomy (upstream_test_fixture, AccessDeniedException, authentication_error, etc.) to customers.

Contract correction

FieldSourceWhy
error.typeDP taxonomy: "upstream_error"Stable, customer-SDK-actionable, hides upstream zoo
error.messageupstream verbatim (4xx) / canned (5xx)Human-readable, already correct
error.codeupstream verbatim or derived per-wireSDK retry branches on this — preservation from #322 stays
error.paramupstream verbatimTells client which field caused the error — preservation from #322 stays

Customer SDKs branch on error.type == "upstream_error" for upstream-class detection and on error.code for granular retry routing.

Implementation

  • error_translate::render_openai_envelope: hardcodes kind to a new UPSTREAM_ERROR_TYPE constant ("upstream_error").
  • Translation tables collapse from (String, Option<String>) to Option<String> — the per-wire OpenAI type half is now uniformly "upstream_error", so the tables only carry the derived OpenAI string code.
  • 5xx, UpstreamWire::Unknown, and generic-envelope paths already emit "upstream_error" — this commit just makes the 4xx path consistent.

Reference impl divergence (CLAUDE.md §7)

The established gateway impls in this space normalise upstream taxonomy too — LiteLLM maps to a closed set of Python exception classes, Portkey emits its own stable type enum. Choosing a single DP token ("upstream_error") rather than a closed OpenAI-vocabulary set (invalid_request_error, rate_limit_exceeded, server_error) is more conservative — it sidesteps the question of how Bedrock's AccessDeniedException should map onto OpenAI's invalid_request_error vs authentication_error, since the customer just needs "upstream broke; check code for granularity."

Test plan

  • cargo check --workspace --all-targets clean
  • cargo clippy --workspace --all-targets clean
  • cargo fmt --check clean
  • cargo test --workspace — all tests green
  • 22 error_translate::tests updated: body.kind always "upstream_error", code assertions unchanged
  • 3 affected integration tests in aisix-proxy::tests updated and renamed to reflect the corrected contract:
    • upstream_openai_4xx_forwards_code_and_param_but_normalises_type_per_issue_327 (was ..._forwards_full_envelope_per_issue_322)
    • upstream_anthropic_400_normalises_type_to_upstream_error (was ..._passes_through_with_openai_envelope)
    • upstream_anthropic_rate_limit_derives_openai_rate_limit_exceeded_code (was ..._translates_to_openai_rate_limit_exceeded)
  • dp-upstream-error-live.spec.ts:152 should go back to green once :dev image is rebuilt with this PR merged.
  • AISIX-Cloud matrix spec tightening (adapter-openai-overrides-live.spec.ts:634 from upstream_test_fixture to upstream_error) is the issue author's follow-up.

Closes#327

Summary by CodeRabbit

  • Bug Fixes
    • Standardized upstream service error responses to use consistent error type formatting across all provider services, preventing provider-specific error taxonomy from being exposed to client applications.
    • Enhanced error code mapping to improve SDK compatibility and ensure proper retry routing behavior across integrated providers.

Review Change Stack

…/param verbatim (#327)
#323 over-corrected by preserving upstream's `error.type` verbatim,
which:
- broke `dp-upstream-error-live.spec.ts:152` (asserting the stable
`"upstream_error"` token) on every PR's e2e-playwright run from
the merge forward
- leaked upstream-private taxonomy to customers (mock-llm's
`upstream_test_fixture`, Bedrock's `AccessDeniedException`,
Anthropic's `authentication_error` etc.)
- defeated the gateway's role as a normalising layer that hides
the upstream zoo behind a stable, customer-SDK-actionable
taxonomy
This commit restores the DP-stable `error.type = "upstream_error"`
contract for upstream errors, while keeping the #322/#323
preservation of `error.code` and `error.param` — those are still
SDK-actionable and customer-facing-positive.
Final wire contract for `ProxyError::Bridge(UpstreamStatus { .. })`:
| Field | Source |
|----------------|---------------------------------|
| `error.type` | DP taxonomy: `"upstream_error"` |
| `error.message`| upstream verbatim (4xx) / canned `"upstream returned N"` (5xx) |
| `error.code` | upstream verbatim or derived per-wire (Anthropic/Bedrock/Vertex/Azure) |
| `error.param` | upstream verbatim |
Customer SDKs branch on `error.type == "upstream_error"` for
upstream-class detection and on `error.code` for granular retry
routing (`rate_limit_exceeded` vs `insufficient_quota` vs
`model_not_found` etc.).
Implementation:
* `error_translate::render_openai_envelope` hardcodes `kind` to the
new `UPSTREAM_ERROR_TYPE` constant. The translation tables
(Anthropic / Bedrock / Vertex / Azure) collapse from returning
`(String, Option<String>)` to just `Option<String>` — the type
half is now uniformly `"upstream_error"`, so the per-wire tables
only carry the derived OpenAI string code.
* Generic envelope (missing parsed view) also emits
`"upstream_error"` — unchanged from prior behaviour.
* 5xx and `UpstreamWire::Unknown` paths in
`render_bridge_upstream_envelope` already emit `"upstream_error"`;
this commit makes the 4xx path consistent.
Tests:
* All 22 `error_translate::tests` updated: `body.kind` assertions
pinned to `"upstream_error"`; code assertions unchanged.
* 3 integration tests in `aisix-proxy::tests` updated and renamed:
- `upstream_openai_4xx_forwards_full_envelope_per_issue_322` →
`upstream_openai_4xx_forwards_code_and_param_but_normalises_type_per_issue_327`
(asserts `upstream_test_fixture` does NOT leak)
- `upstream_anthropic_400_passes_through_with_openai_envelope` →
`upstream_anthropic_400_normalises_type_to_upstream_error`
- `upstream_anthropic_rate_limit_translates_to_openai_rate_limit_exceeded`
→
`upstream_anthropic_rate_limit_derives_openai_rate_limit_exceeded_code`
(the rate-limit `code` derivation is the customer-facing value;
the `type` is normalised)
Closes#327
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
CopilotAI review requested due to automatic review settings May 18, 2026 05:19
@coderabbitai

coderabbitaiBot commented May 18, 2026

Copy link
Copy Markdown
ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Free

Run ID: 443cdddd-c528-4f52-95ac-29328f6b5e15

📥 Commits

Reviewing files that changed from the base of the PR and between 1a744ee and 0fe40fc.

📒 Files selected for processing (2)
  • crates/aisix-proxy/src/error_translate.rs
  • crates/aisix-proxy/src/lib.rs

📝 Walkthrough

Walkthrough

This PR normalizes upstream error type handling in the proxy: error.type is always set to the DP-stable "upstream_error" token for upstream-originated 4xx errors, while error.code is intelligently derived via provider-specific helpers that map upstream taxonomies to OpenAI-compatible strings, with fallback preservation of upstream codes when derivation is unavailable.

Changes

Error type normalization and code derivation

Layer / File(s)Summary
Documentation and envelope rendering refactoring
crates/aisix-proxy/src/error_translate.rs
Module documentation updated to describe DP-stable error type normalization. The render_openai_envelope function refactored to always set kind to fixed "upstream_error" token and compute derived OpenAI code via per-upstream match logic that prefers derived codes for non-OpenAI wires while preserving upstream codes for OpenAI same-wire.
Error code derivation helpers
crates/aisix-proxy/src/error_translate.rs
Introduced UPSTREAM_ERROR_TYPE constant, updated generic envelope builder, and new derive_anthropic_code, derive_bedrock_code, derive_vertex_code, and derive_azure_code functions returning Option<String> for provider-specific OpenAI-compatible code mappings; Azure falls back to upstream code when derivation returns None.
Anthropic and Bedrock error handling tests
crates/aisix-proxy/src/error_translate.rs
Updated and added unit tests for Anthropic error kinds and Bedrock throttling to assert kind == "upstream_error" while validating derived code values (including None for unmapped kinds).
Bedrock, Vertex, and Azure error mapping tests
crates/aisix-proxy/src/error_translate.rs
Expanded tests for Bedrock quota/validation/access errors, Vertex gRPC status-code mappings, and Azure DeploymentNotFound to verify normalized kind and provider-specific derived code values.
Azure/OpenAI compatibility and same-wire preservation tests
crates/aisix-proxy/src/error_translate.rs
Tests for Azure content-policy violation derivation, Azure OpenAI-compatible code fallback behavior, OpenAI same-wire preservation of both code and param with normalized kind, and missing-parsed-message derived code assertion.
Cross-provider contract tests in lib.rs
crates/aisix-proxy/src/lib.rs
Integration-level tests verifying that OpenAI upstream 4xx and Anthropic 400/rate-limit errors enforce normalized error.type == "upstream_error" at the proxy boundary while preserving granular error.code and error.param for SDK retry branching and Anthropic→OpenAI code derivation.

🎯 3 (Moderate) | ⏱️ ~25 minutes


Note

🎁 Summarized by CodeRabbit Free

Your organization has reached its limit of developer seats under the Pro Plan. For new users, CodeRabbit will generate a high-level summary and a walkthrough for each pull request. For a comprehensive line-by-line review, please add seats to your subscription by visiting https://app.coderabbit.ai/login.If you believe this is a mistake and have available seats, please assign one to the pull request author through the subscription management page using the link above.

Comment @coderabbitai help to get the list of available commands and usage tips.

CopilotAI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR corrects an over-correction from #323: while error.code and error.param are still preserved verbatim from upstream (preserving #322's SDK retry-routing fix), error.type is now normalized to the DP-stable "upstream_error" token across all wires/paths, so upstream-private taxonomies (upstream_test_fixture, AccessDeniedException, authentication_error, etc.) no longer leak to customers. The translation tables in error_translate.rs are collapsed from (type, code) pairs to a single derived code.

Changes:

  • Hardcode kind = "upstream_error" in render_openai_envelope; introduce UPSTREAM_ERROR_TYPE constant.
  • Rename translate_*derive_*_code and return Option<String> only (the OpenAI-shape code).
  • Update unit and integration tests + their docstrings/names to assert the corrected contract.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated no comments.

FileDescription
crates/aisix-proxy/src/error_translate.rsDrop per-wire type derivation; emit DP-stable upstream_error and only derive OpenAI code. Update module doc and tests.
crates/aisix-proxy/src/lib.rsRename/retighten 3 integration tests to assert type == "upstream_error" while keeping code/param preservation assertions.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@moonming

moonming commented May 18, 2026

Copy link
Copy Markdown
MemberAuthor

Audit completed — verdict: merge, no HIGH/MEDIUM findings.

Two LOW findings:

Per CLAUDE.md §8 merge gate: ready when CI passes.

@moonming

Copy link
Copy Markdown
MemberAuthor

Follow-up on the audit's LOW-1: traced the path, not a real bug. BridgeError::UpstreamStatus is only constructed pre-stream (via map_http_error), and the stream itself only yields Transport/UpstreamDecode whose Display impls don't carry upstream body content. Closed #329 with the trace.

@moonming
moonming merged commit 30f70a2 into mainMay 18, 2026
12 checks passed
@moonming
moonming deleted the fix/error-type-normalize-327 branch May 18, 2026 05:30
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

bug(proxy): #322 over-corrected — error.type should normalize to DP taxonomy, not verbatim from upstream

2 participants

@moonming
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

fix(proxy): normalise upstream error.type to DP taxonomy, keep code/param verbatim (#327) - #328

Merged
moonming merged 1 commit into
mainfrom
fix/error-type-normalize-327
May 18, 2026
Merged

fix(proxy): normalise upstream error.type to DP taxonomy, keep code/param verbatim (#327)#328
moonming merged 1 commit into
mainfrom
fix/error-type-normalize-327

Conversation

@moonming

@moonmingmoonming commented May 18, 2026

Copy link
Copy Markdown
Member

Summary

Fixes#327. PR #323 over-corrected by preserving upstream's error.type verbatim — this broke the stable "upstream_error" contract that dp-upstream-error-live.spec.ts:152 already pins, and leaked upstream-private taxonomy (upstream_test_fixture, AccessDeniedException, authentication_error, etc.) to customers.

Contract correction

FieldSourceWhy
error.typeDP taxonomy: "upstream_error"Stable, customer-SDK-actionable, hides upstream zoo
error.messageupstream verbatim (4xx) / canned (5xx)Human-readable, already correct
error.codeupstream verbatim or derived per-wireSDK retry branches on this — preservation from #322 stays
error.paramupstream verbatimTells client which field caused the error — preservation from #322 stays

Customer SDKs branch on error.type == "upstream_error" for upstream-class detection and on error.code for granular retry routing.

Implementation

  • error_translate::render_openai_envelope: hardcodes kind to a new UPSTREAM_ERROR_TYPE constant ("upstream_error").
  • Translation tables collapse from (String, Option<String>) to Option<String> — the per-wire OpenAI type half is now uniformly "upstream_error", so the tables only carry the derived OpenAI string code.
  • 5xx, UpstreamWire::Unknown, and generic-envelope paths already emit "upstream_error" — this commit just makes the 4xx path consistent.

Reference impl divergence (CLAUDE.md §7)

The established gateway impls in this space normalise upstream taxonomy too — LiteLLM maps to a closed set of Python exception classes, Portkey emits its own stable type enum. Choosing a single DP token ("upstream_error") rather than a closed OpenAI-vocabulary set (invalid_request_error, rate_limit_exceeded, server_error) is more conservative — it sidesteps the question of how Bedrock's AccessDeniedException should map onto OpenAI's invalid_request_error vs authentication_error, since the customer just needs "upstream broke; check code for granularity."

Test plan

  • cargo check --workspace --all-targets clean
  • cargo clippy --workspace --all-targets clean
  • cargo fmt --check clean
  • cargo test --workspace — all tests green
  • 22 error_translate::tests updated: body.kind always "upstream_error", code assertions unchanged
  • 3 affected integration tests in aisix-proxy::tests updated and renamed to reflect the corrected contract:
    • upstream_openai_4xx_forwards_code_and_param_but_normalises_type_per_issue_327 (was ..._forwards_full_envelope_per_issue_322)
    • upstream_anthropic_400_normalises_type_to_upstream_error (was ..._passes_through_with_openai_envelope)
    • upstream_anthropic_rate_limit_derives_openai_rate_limit_exceeded_code (was ..._translates_to_openai_rate_limit_exceeded)
  • dp-upstream-error-live.spec.ts:152 should go back to green once :dev image is rebuilt with this PR merged.
  • AISIX-Cloud matrix spec tightening (adapter-openai-overrides-live.spec.ts:634 from upstream_test_fixture to upstream_error) is the issue author's follow-up.

Closes#327

Summary by CodeRabbit

  • Bug Fixes
    • Standardized upstream service error responses to use consistent error type formatting across all provider services, preventing provider-specific error taxonomy from being exposed to client applications.
    • Enhanced error code mapping to improve SDK compatibility and ensure proper retry routing behavior across integrated providers.

Review Change Stack

…/param verbatim (#327)
#323 over-corrected by preserving upstream's `error.type` verbatim,
which:
- broke `dp-upstream-error-live.spec.ts:152` (asserting the stable
`"upstream_error"` token) on every PR's e2e-playwright run from
the merge forward
- leaked upstream-private taxonomy to customers (mock-llm's
`upstream_test_fixture`, Bedrock's `AccessDeniedException`,
Anthropic's `authentication_error` etc.)
- defeated the gateway's role as a normalising layer that hides
the upstream zoo behind a stable, customer-SDK-actionable
taxonomy
This commit restores the DP-stable `error.type = "upstream_error"`
contract for upstream errors, while keeping the #322/#323
preservation of `error.code` and `error.param` — those are still
SDK-actionable and customer-facing-positive.
Final wire contract for `ProxyError::Bridge(UpstreamStatus { .. })`:
| Field | Source |
|----------------|---------------------------------|
| `error.type` | DP taxonomy: `"upstream_error"` |
| `error.message`| upstream verbatim (4xx) / canned `"upstream returned N"` (5xx) |
| `error.code` | upstream verbatim or derived per-wire (Anthropic/Bedrock/Vertex/Azure) |
| `error.param` | upstream verbatim |
Customer SDKs branch on `error.type == "upstream_error"` for
upstream-class detection and on `error.code` for granular retry
routing (`rate_limit_exceeded` vs `insufficient_quota` vs
`model_not_found` etc.).
Implementation:
* `error_translate::render_openai_envelope` hardcodes `kind` to the
new `UPSTREAM_ERROR_TYPE` constant. The translation tables
(Anthropic / Bedrock / Vertex / Azure) collapse from returning
`(String, Option<String>)` to just `Option<String>` — the type
half is now uniformly `"upstream_error"`, so the per-wire tables
only carry the derived OpenAI string code.
* Generic envelope (missing parsed view) also emits
`"upstream_error"` — unchanged from prior behaviour.
* 5xx and `UpstreamWire::Unknown` paths in
`render_bridge_upstream_envelope` already emit `"upstream_error"`;
this commit makes the 4xx path consistent.
Tests:
* All 22 `error_translate::tests` updated: `body.kind` assertions
pinned to `"upstream_error"`; code assertions unchanged.
* 3 integration tests in `aisix-proxy::tests` updated and renamed:
- `upstream_openai_4xx_forwards_full_envelope_per_issue_322` →
`upstream_openai_4xx_forwards_code_and_param_but_normalises_type_per_issue_327`
(asserts `upstream_test_fixture` does NOT leak)
- `upstream_anthropic_400_passes_through_with_openai_envelope` →
`upstream_anthropic_400_normalises_type_to_upstream_error`
- `upstream_anthropic_rate_limit_translates_to_openai_rate_limit_exceeded`
→
`upstream_anthropic_rate_limit_derives_openai_rate_limit_exceeded_code`
(the rate-limit `code` derivation is the customer-facing value;
the `type` is normalised)
Closes#327
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
CopilotAI review requested due to automatic review settings May 18, 2026 05:19
@coderabbitai

coderabbitaiBot commented May 18, 2026

Copy link
Copy Markdown
ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Free

Run ID: 443cdddd-c528-4f52-95ac-29328f6b5e15

📥 Commits

Reviewing files that changed from the base of the PR and between 1a744ee and 0fe40fc.

📒 Files selected for processing (2)
  • crates/aisix-proxy/src/error_translate.rs
  • crates/aisix-proxy/src/lib.rs

📝 Walkthrough

Walkthrough

This PR normalizes upstream error type handling in the proxy: error.type is always set to the DP-stable "upstream_error" token for upstream-originated 4xx errors, while error.code is intelligently derived via provider-specific helpers that map upstream taxonomies to OpenAI-compatible strings, with fallback preservation of upstream codes when derivation is unavailable.

Changes

Error type normalization and code derivation

Layer / File(s)Summary
Documentation and envelope rendering refactoring
crates/aisix-proxy/src/error_translate.rs
Module documentation updated to describe DP-stable error type normalization. The render_openai_envelope function refactored to always set kind to fixed "upstream_error" token and compute derived OpenAI code via per-upstream match logic that prefers derived codes for non-OpenAI wires while preserving upstream codes for OpenAI same-wire.
Error code derivation helpers
crates/aisix-proxy/src/error_translate.rs
Introduced UPSTREAM_ERROR_TYPE constant, updated generic envelope builder, and new derive_anthropic_code, derive_bedrock_code, derive_vertex_code, and derive_azure_code functions returning Option<String> for provider-specific OpenAI-compatible code mappings; Azure falls back to upstream code when derivation returns None.
Anthropic and Bedrock error handling tests
crates/aisix-proxy/src/error_translate.rs
Updated and added unit tests for Anthropic error kinds and Bedrock throttling to assert kind == "upstream_error" while validating derived code values (including None for unmapped kinds).
Bedrock, Vertex, and Azure error mapping tests
crates/aisix-proxy/src/error_translate.rs
Expanded tests for Bedrock quota/validation/access errors, Vertex gRPC status-code mappings, and Azure DeploymentNotFound to verify normalized kind and provider-specific derived code values.
Azure/OpenAI compatibility and same-wire preservation tests
crates/aisix-proxy/src/error_translate.rs
Tests for Azure content-policy violation derivation, Azure OpenAI-compatible code fallback behavior, OpenAI same-wire preservation of both code and param with normalized kind, and missing-parsed-message derived code assertion.
Cross-provider contract tests in lib.rs
crates/aisix-proxy/src/lib.rs
Integration-level tests verifying that OpenAI upstream 4xx and Anthropic 400/rate-limit errors enforce normalized error.type == "upstream_error" at the proxy boundary while preserving granular error.code and error.param for SDK retry branching and Anthropic→OpenAI code derivation.

🎯 3 (Moderate) | ⏱️ ~25 minutes


Note

🎁 Summarized by CodeRabbit Free

Your organization has reached its limit of developer seats under the Pro Plan. For new users, CodeRabbit will generate a high-level summary and a walkthrough for each pull request. For a comprehensive line-by-line review, please add seats to your subscription by visiting https://app.coderabbit.ai/login.If you believe this is a mistake and have available seats, please assign one to the pull request author through the subscription management page using the link above.

Comment @coderabbitai help to get the list of available commands and usage tips.

CopilotAI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR corrects an over-correction from #323: while error.code and error.param are still preserved verbatim from upstream (preserving #322's SDK retry-routing fix), error.type is now normalized to the DP-stable "upstream_error" token across all wires/paths, so upstream-private taxonomies (upstream_test_fixture, AccessDeniedException, authentication_error, etc.) no longer leak to customers. The translation tables in error_translate.rs are collapsed from (type, code) pairs to a single derived code.

Changes:

  • Hardcode kind = "upstream_error" in render_openai_envelope; introduce UPSTREAM_ERROR_TYPE constant.
  • Rename translate_*derive_*_code and return Option<String> only (the OpenAI-shape code).
  • Update unit and integration tests + their docstrings/names to assert the corrected contract.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated no comments.

FileDescription
crates/aisix-proxy/src/error_translate.rsDrop per-wire type derivation; emit DP-stable upstream_error and only derive OpenAI code. Update module doc and tests.
crates/aisix-proxy/src/lib.rsRename/retighten 3 integration tests to assert type == "upstream_error" while keeping code/param preservation assertions.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@moonming

moonming commented May 18, 2026

Copy link
Copy Markdown
MemberAuthor

Audit completed — verdict: merge, no HIGH/MEDIUM findings.

Two LOW findings:

Per CLAUDE.md §8 merge gate: ready when CI passes.

@moonming

Copy link
Copy Markdown
MemberAuthor

Follow-up on the audit's LOW-1: traced the path, not a real bug. BridgeError::UpstreamStatus is only constructed pre-stream (via map_http_error), and the stream itself only yields Transport/UpstreamDecode whose Display impls don't carry upstream body content. Closed #329 with the trace.

@moonming
moonming merged commit 30f70a2 into mainMay 18, 2026
12 checks passed
@moonming
moonming deleted the fix/error-type-normalize-327 branch May 18, 2026 05:30
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

bug(proxy): #322 over-corrected — error.type should normalize to DP taxonomy, not verbatim from upstream

2 participants

@moonming
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

fix(proxy): normalise upstream error.type to DP taxonomy, keep code/param verbatim (#327) - #328

Merged
moonming merged 1 commit into
mainfrom
fix/error-type-normalize-327
May 18, 2026
Merged

fix(proxy): normalise upstream error.type to DP taxonomy, keep code/param verbatim (#327)#328
moonming merged 1 commit into
mainfrom
fix/error-type-normalize-327

Conversation

@moonming

@moonmingmoonming commented May 18, 2026

Copy link
Copy Markdown
Member

Summary

Fixes#327. PR #323 over-corrected by preserving upstream's error.type verbatim — this broke the stable "upstream_error" contract that dp-upstream-error-live.spec.ts:152 already pins, and leaked upstream-private taxonomy (upstream_test_fixture, AccessDeniedException, authentication_error, etc.) to customers.

Contract correction

FieldSourceWhy
error.typeDP taxonomy: "upstream_error"Stable, customer-SDK-actionable, hides upstream zoo
error.messageupstream verbatim (4xx) / canned (5xx)Human-readable, already correct
error.codeupstream verbatim or derived per-wireSDK retry branches on this — preservation from #322 stays
error.paramupstream verbatimTells client which field caused the error — preservation from #322 stays

Customer SDKs branch on error.type == "upstream_error" for upstream-class detection and on error.code for granular retry routing.

Implementation

  • error_translate::render_openai_envelope: hardcodes kind to a new UPSTREAM_ERROR_TYPE constant ("upstream_error").
  • Translation tables collapse from (String, Option<String>) to Option<String> — the per-wire OpenAI type half is now uniformly "upstream_error", so the tables only carry the derived OpenAI string code.
  • 5xx, UpstreamWire::Unknown, and generic-envelope paths already emit "upstream_error" — this commit just makes the 4xx path consistent.

Reference impl divergence (CLAUDE.md §7)

The established gateway impls in this space normalise upstream taxonomy too — LiteLLM maps to a closed set of Python exception classes, Portkey emits its own stable type enum. Choosing a single DP token ("upstream_error") rather than a closed OpenAI-vocabulary set (invalid_request_error, rate_limit_exceeded, server_error) is more conservative — it sidesteps the question of how Bedrock's AccessDeniedException should map onto OpenAI's invalid_request_error vs authentication_error, since the customer just needs "upstream broke; check code for granularity."

Test plan

  • cargo check --workspace --all-targets clean
  • cargo clippy --workspace --all-targets clean
  • cargo fmt --check clean
  • cargo test --workspace — all tests green
  • 22 error_translate::tests updated: body.kind always "upstream_error", code assertions unchanged
  • 3 affected integration tests in aisix-proxy::tests updated and renamed to reflect the corrected contract:
    • upstream_openai_4xx_forwards_code_and_param_but_normalises_type_per_issue_327 (was ..._forwards_full_envelope_per_issue_322)
    • upstream_anthropic_400_normalises_type_to_upstream_error (was ..._passes_through_with_openai_envelope)
    • upstream_anthropic_rate_limit_derives_openai_rate_limit_exceeded_code (was ..._translates_to_openai_rate_limit_exceeded)
  • dp-upstream-error-live.spec.ts:152 should go back to green once :dev image is rebuilt with this PR merged.
  • AISIX-Cloud matrix spec tightening (adapter-openai-overrides-live.spec.ts:634 from upstream_test_fixture to upstream_error) is the issue author's follow-up.

Closes#327

Summary by CodeRabbit

  • Bug Fixes
    • Standardized upstream service error responses to use consistent error type formatting across all provider services, preventing provider-specific error taxonomy from being exposed to client applications.
    • Enhanced error code mapping to improve SDK compatibility and ensure proper retry routing behavior across integrated providers.

Review Change Stack

…/param verbatim (#327)
#323 over-corrected by preserving upstream's `error.type` verbatim,
which:
- broke `dp-upstream-error-live.spec.ts:152` (asserting the stable
`"upstream_error"` token) on every PR's e2e-playwright run from
the merge forward
- leaked upstream-private taxonomy to customers (mock-llm's
`upstream_test_fixture`, Bedrock's `AccessDeniedException`,
Anthropic's `authentication_error` etc.)
- defeated the gateway's role as a normalising layer that hides
the upstream zoo behind a stable, customer-SDK-actionable
taxonomy
This commit restores the DP-stable `error.type = "upstream_error"`
contract for upstream errors, while keeping the #322/#323
preservation of `error.code` and `error.param` — those are still
SDK-actionable and customer-facing-positive.
Final wire contract for `ProxyError::Bridge(UpstreamStatus { .. })`:
| Field | Source |
|----------------|---------------------------------|
| `error.type` | DP taxonomy: `"upstream_error"` |
| `error.message`| upstream verbatim (4xx) / canned `"upstream returned N"` (5xx) |
| `error.code` | upstream verbatim or derived per-wire (Anthropic/Bedrock/Vertex/Azure) |
| `error.param` | upstream verbatim |
Customer SDKs branch on `error.type == "upstream_error"` for
upstream-class detection and on `error.code` for granular retry
routing (`rate_limit_exceeded` vs `insufficient_quota` vs
`model_not_found` etc.).
Implementation:
* `error_translate::render_openai_envelope` hardcodes `kind` to the
new `UPSTREAM_ERROR_TYPE` constant. The translation tables
(Anthropic / Bedrock / Vertex / Azure) collapse from returning
`(String, Option<String>)` to just `Option<String>` — the type
half is now uniformly `"upstream_error"`, so the per-wire tables
only carry the derived OpenAI string code.
* Generic envelope (missing parsed view) also emits
`"upstream_error"` — unchanged from prior behaviour.
* 5xx and `UpstreamWire::Unknown` paths in
`render_bridge_upstream_envelope` already emit `"upstream_error"`;
this commit makes the 4xx path consistent.
Tests:
* All 22 `error_translate::tests` updated: `body.kind` assertions
pinned to `"upstream_error"`; code assertions unchanged.
* 3 integration tests in `aisix-proxy::tests` updated and renamed:
- `upstream_openai_4xx_forwards_full_envelope_per_issue_322` →
`upstream_openai_4xx_forwards_code_and_param_but_normalises_type_per_issue_327`
(asserts `upstream_test_fixture` does NOT leak)
- `upstream_anthropic_400_passes_through_with_openai_envelope` →
`upstream_anthropic_400_normalises_type_to_upstream_error`
- `upstream_anthropic_rate_limit_translates_to_openai_rate_limit_exceeded`
→
`upstream_anthropic_rate_limit_derives_openai_rate_limit_exceeded_code`
(the rate-limit `code` derivation is the customer-facing value;
the `type` is normalised)
Closes#327
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
CopilotAI review requested due to automatic review settings May 18, 2026 05:19
@coderabbitai

coderabbitaiBot commented May 18, 2026

Copy link
Copy Markdown
ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Free

Run ID: 443cdddd-c528-4f52-95ac-29328f6b5e15

📥 Commits

Reviewing files that changed from the base of the PR and between 1a744ee and 0fe40fc.

📒 Files selected for processing (2)
  • crates/aisix-proxy/src/error_translate.rs
  • crates/aisix-proxy/src/lib.rs

📝 Walkthrough

Walkthrough

This PR normalizes upstream error type handling in the proxy: error.type is always set to the DP-stable "upstream_error" token for upstream-originated 4xx errors, while error.code is intelligently derived via provider-specific helpers that map upstream taxonomies to OpenAI-compatible strings, with fallback preservation of upstream codes when derivation is unavailable.

Changes

Error type normalization and code derivation

Layer / File(s)Summary
Documentation and envelope rendering refactoring
crates/aisix-proxy/src/error_translate.rs
Module documentation updated to describe DP-stable error type normalization. The render_openai_envelope function refactored to always set kind to fixed "upstream_error" token and compute derived OpenAI code via per-upstream match logic that prefers derived codes for non-OpenAI wires while preserving upstream codes for OpenAI same-wire.
Error code derivation helpers
crates/aisix-proxy/src/error_translate.rs
Introduced UPSTREAM_ERROR_TYPE constant, updated generic envelope builder, and new derive_anthropic_code, derive_bedrock_code, derive_vertex_code, and derive_azure_code functions returning Option<String> for provider-specific OpenAI-compatible code mappings; Azure falls back to upstream code when derivation returns None.
Anthropic and Bedrock error handling tests
crates/aisix-proxy/src/error_translate.rs
Updated and added unit tests for Anthropic error kinds and Bedrock throttling to assert kind == "upstream_error" while validating derived code values (including None for unmapped kinds).
Bedrock, Vertex, and Azure error mapping tests
crates/aisix-proxy/src/error_translate.rs
Expanded tests for Bedrock quota/validation/access errors, Vertex gRPC status-code mappings, and Azure DeploymentNotFound to verify normalized kind and provider-specific derived code values.
Azure/OpenAI compatibility and same-wire preservation tests
crates/aisix-proxy/src/error_translate.rs
Tests for Azure content-policy violation derivation, Azure OpenAI-compatible code fallback behavior, OpenAI same-wire preservation of both code and param with normalized kind, and missing-parsed-message derived code assertion.
Cross-provider contract tests in lib.rs
crates/aisix-proxy/src/lib.rs
Integration-level tests verifying that OpenAI upstream 4xx and Anthropic 400/rate-limit errors enforce normalized error.type == "upstream_error" at the proxy boundary while preserving granular error.code and error.param for SDK retry branching and Anthropic→OpenAI code derivation.

🎯 3 (Moderate) | ⏱️ ~25 minutes


Note

🎁 Summarized by CodeRabbit Free

Your organization has reached its limit of developer seats under the Pro Plan. For new users, CodeRabbit will generate a high-level summary and a walkthrough for each pull request. For a comprehensive line-by-line review, please add seats to your subscription by visiting https://app.coderabbit.ai/login.If you believe this is a mistake and have available seats, please assign one to the pull request author through the subscription management page using the link above.

Comment @coderabbitai help to get the list of available commands and usage tips.

CopilotAI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR corrects an over-correction from #323: while error.code and error.param are still preserved verbatim from upstream (preserving #322's SDK retry-routing fix), error.type is now normalized to the DP-stable "upstream_error" token across all wires/paths, so upstream-private taxonomies (upstream_test_fixture, AccessDeniedException, authentication_error, etc.) no longer leak to customers. The translation tables in error_translate.rs are collapsed from (type, code) pairs to a single derived code.

Changes:

  • Hardcode kind = "upstream_error" in render_openai_envelope; introduce UPSTREAM_ERROR_TYPE constant.
  • Rename translate_*derive_*_code and return Option<String> only (the OpenAI-shape code).
  • Update unit and integration tests + their docstrings/names to assert the corrected contract.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated no comments.

FileDescription
crates/aisix-proxy/src/error_translate.rsDrop per-wire type derivation; emit DP-stable upstream_error and only derive OpenAI code. Update module doc and tests.
crates/aisix-proxy/src/lib.rsRename/retighten 3 integration tests to assert type == "upstream_error" while keeping code/param preservation assertions.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@moonming

moonming commented May 18, 2026

Copy link
Copy Markdown
MemberAuthor

Audit completed — verdict: merge, no HIGH/MEDIUM findings.

Two LOW findings:

Per CLAUDE.md §8 merge gate: ready when CI passes.

@moonming

Copy link
Copy Markdown
MemberAuthor

Follow-up on the audit's LOW-1: traced the path, not a real bug. BridgeError::UpstreamStatus is only constructed pre-stream (via map_http_error), and the stream itself only yields Transport/UpstreamDecode whose Display impls don't carry upstream body content. Closed #329 with the trace.

@moonming
moonming merged commit 30f70a2 into mainMay 18, 2026
12 checks passed
@moonming
moonming deleted the fix/error-type-normalize-327 branch May 18, 2026 05:30
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

bug(proxy): #322 over-corrected — error.type should normalize to DP taxonomy, not verbatim from upstream

2 participants

@moonming
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

fix(proxy): normalise upstream error.type to DP taxonomy, keep code/param verbatim (#327) - #328

Merged
moonming merged 1 commit into
mainfrom
fix/error-type-normalize-327
May 18, 2026
Merged

fix(proxy): normalise upstream error.type to DP taxonomy, keep code/param verbatim (#327)#328
moonming merged 1 commit into
mainfrom
fix/error-type-normalize-327

Conversation

@moonming

@moonmingmoonming commented May 18, 2026

Copy link
Copy Markdown
Member

Summary

Fixes#327. PR #323 over-corrected by preserving upstream's error.type verbatim — this broke the stable "upstream_error" contract that dp-upstream-error-live.spec.ts:152 already pins, and leaked upstream-private taxonomy (upstream_test_fixture, AccessDeniedException, authentication_error, etc.) to customers.

Contract correction

FieldSourceWhy
error.typeDP taxonomy: "upstream_error"Stable, customer-SDK-actionable, hides upstream zoo
error.messageupstream verbatim (4xx) / canned (5xx)Human-readable, already correct
error.codeupstream verbatim or derived per-wireSDK retry branches on this — preservation from #322 stays
error.paramupstream verbatimTells client which field caused the error — preservation from #322 stays

Customer SDKs branch on error.type == "upstream_error" for upstream-class detection and on error.code for granular retry routing.

Implementation

  • error_translate::render_openai_envelope: hardcodes kind to a new UPSTREAM_ERROR_TYPE constant ("upstream_error").
  • Translation tables collapse from (String, Option<String>) to Option<String> — the per-wire OpenAI type half is now uniformly "upstream_error", so the tables only carry the derived OpenAI string code.
  • 5xx, UpstreamWire::Unknown, and generic-envelope paths already emit "upstream_error" — this commit just makes the 4xx path consistent.

Reference impl divergence (CLAUDE.md §7)

The established gateway impls in this space normalise upstream taxonomy too — LiteLLM maps to a closed set of Python exception classes, Portkey emits its own stable type enum. Choosing a single DP token ("upstream_error") rather than a closed OpenAI-vocabulary set (invalid_request_error, rate_limit_exceeded, server_error) is more conservative — it sidesteps the question of how Bedrock's AccessDeniedException should map onto OpenAI's invalid_request_error vs authentication_error, since the customer just needs "upstream broke; check code for granularity."

Test plan

  • cargo check --workspace --all-targets clean
  • cargo clippy --workspace --all-targets clean
  • cargo fmt --check clean
  • cargo test --workspace — all tests green
  • 22 error_translate::tests updated: body.kind always "upstream_error", code assertions unchanged
  • 3 affected integration tests in aisix-proxy::tests updated and renamed to reflect the corrected contract:
    • upstream_openai_4xx_forwards_code_and_param_but_normalises_type_per_issue_327 (was ..._forwards_full_envelope_per_issue_322)
    • upstream_anthropic_400_normalises_type_to_upstream_error (was ..._passes_through_with_openai_envelope)
    • upstream_anthropic_rate_limit_derives_openai_rate_limit_exceeded_code (was ..._translates_to_openai_rate_limit_exceeded)
  • dp-upstream-error-live.spec.ts:152 should go back to green once :dev image is rebuilt with this PR merged.
  • AISIX-Cloud matrix spec tightening (adapter-openai-overrides-live.spec.ts:634 from upstream_test_fixture to upstream_error) is the issue author's follow-up.

Closes#327

Summary by CodeRabbit

  • Bug Fixes
    • Standardized upstream service error responses to use consistent error type formatting across all provider services, preventing provider-specific error taxonomy from being exposed to client applications.
    • Enhanced error code mapping to improve SDK compatibility and ensure proper retry routing behavior across integrated providers.

Review Change Stack

…/param verbatim (#327)
#323 over-corrected by preserving upstream's `error.type` verbatim,
which:
- broke `dp-upstream-error-live.spec.ts:152` (asserting the stable
`"upstream_error"` token) on every PR's e2e-playwright run from
the merge forward
- leaked upstream-private taxonomy to customers (mock-llm's
`upstream_test_fixture`, Bedrock's `AccessDeniedException`,
Anthropic's `authentication_error` etc.)
- defeated the gateway's role as a normalising layer that hides
the upstream zoo behind a stable, customer-SDK-actionable
taxonomy
This commit restores the DP-stable `error.type = "upstream_error"`
contract for upstream errors, while keeping the #322/#323
preservation of `error.code` and `error.param` — those are still
SDK-actionable and customer-facing-positive.
Final wire contract for `ProxyError::Bridge(UpstreamStatus { .. })`:
| Field | Source |
|----------------|---------------------------------|
| `error.type` | DP taxonomy: `"upstream_error"` |
| `error.message`| upstream verbatim (4xx) / canned `"upstream returned N"` (5xx) |
| `error.code` | upstream verbatim or derived per-wire (Anthropic/Bedrock/Vertex/Azure) |
| `error.param` | upstream verbatim |
Customer SDKs branch on `error.type == "upstream_error"` for
upstream-class detection and on `error.code` for granular retry
routing (`rate_limit_exceeded` vs `insufficient_quota` vs
`model_not_found` etc.).
Implementation:
* `error_translate::render_openai_envelope` hardcodes `kind` to the
new `UPSTREAM_ERROR_TYPE` constant. The translation tables
(Anthropic / Bedrock / Vertex / Azure) collapse from returning
`(String, Option<String>)` to just `Option<String>` — the type
half is now uniformly `"upstream_error"`, so the per-wire tables
only carry the derived OpenAI string code.
* Generic envelope (missing parsed view) also emits
`"upstream_error"` — unchanged from prior behaviour.
* 5xx and `UpstreamWire::Unknown` paths in
`render_bridge_upstream_envelope` already emit `"upstream_error"`;
this commit makes the 4xx path consistent.
Tests:
* All 22 `error_translate::tests` updated: `body.kind` assertions
pinned to `"upstream_error"`; code assertions unchanged.
* 3 integration tests in `aisix-proxy::tests` updated and renamed:
- `upstream_openai_4xx_forwards_full_envelope_per_issue_322` →
`upstream_openai_4xx_forwards_code_and_param_but_normalises_type_per_issue_327`
(asserts `upstream_test_fixture` does NOT leak)
- `upstream_anthropic_400_passes_through_with_openai_envelope` →
`upstream_anthropic_400_normalises_type_to_upstream_error`
- `upstream_anthropic_rate_limit_translates_to_openai_rate_limit_exceeded`
→
`upstream_anthropic_rate_limit_derives_openai_rate_limit_exceeded_code`
(the rate-limit `code` derivation is the customer-facing value;
the `type` is normalised)
Closes#327
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
CopilotAI review requested due to automatic review settings May 18, 2026 05:19
@coderabbitai

coderabbitaiBot commented May 18, 2026

Copy link
Copy Markdown
ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Free

Run ID: 443cdddd-c528-4f52-95ac-29328f6b5e15

📥 Commits

Reviewing files that changed from the base of the PR and between 1a744ee and 0fe40fc.

📒 Files selected for processing (2)
  • crates/aisix-proxy/src/error_translate.rs
  • crates/aisix-proxy/src/lib.rs

📝 Walkthrough

Walkthrough

This PR normalizes upstream error type handling in the proxy: error.type is always set to the DP-stable "upstream_error" token for upstream-originated 4xx errors, while error.code is intelligently derived via provider-specific helpers that map upstream taxonomies to OpenAI-compatible strings, with fallback preservation of upstream codes when derivation is unavailable.

Changes

Error type normalization and code derivation

Layer / File(s)Summary
Documentation and envelope rendering refactoring
crates/aisix-proxy/src/error_translate.rs
Module documentation updated to describe DP-stable error type normalization. The render_openai_envelope function refactored to always set kind to fixed "upstream_error" token and compute derived OpenAI code via per-upstream match logic that prefers derived codes for non-OpenAI wires while preserving upstream codes for OpenAI same-wire.
Error code derivation helpers
crates/aisix-proxy/src/error_translate.rs
Introduced UPSTREAM_ERROR_TYPE constant, updated generic envelope builder, and new derive_anthropic_code, derive_bedrock_code, derive_vertex_code, and derive_azure_code functions returning Option<String> for provider-specific OpenAI-compatible code mappings; Azure falls back to upstream code when derivation returns None.
Anthropic and Bedrock error handling tests
crates/aisix-proxy/src/error_translate.rs
Updated and added unit tests for Anthropic error kinds and Bedrock throttling to assert kind == "upstream_error" while validating derived code values (including None for unmapped kinds).
Bedrock, Vertex, and Azure error mapping tests
crates/aisix-proxy/src/error_translate.rs
Expanded tests for Bedrock quota/validation/access errors, Vertex gRPC status-code mappings, and Azure DeploymentNotFound to verify normalized kind and provider-specific derived code values.
Azure/OpenAI compatibility and same-wire preservation tests
crates/aisix-proxy/src/error_translate.rs
Tests for Azure content-policy violation derivation, Azure OpenAI-compatible code fallback behavior, OpenAI same-wire preservation of both code and param with normalized kind, and missing-parsed-message derived code assertion.
Cross-provider contract tests in lib.rs
crates/aisix-proxy/src/lib.rs
Integration-level tests verifying that OpenAI upstream 4xx and Anthropic 400/rate-limit errors enforce normalized error.type == "upstream_error" at the proxy boundary while preserving granular error.code and error.param for SDK retry branching and Anthropic→OpenAI code derivation.

🎯 3 (Moderate) | ⏱️ ~25 minutes


Note

🎁 Summarized by CodeRabbit Free

Your organization has reached its limit of developer seats under the Pro Plan. For new users, CodeRabbit will generate a high-level summary and a walkthrough for each pull request. For a comprehensive line-by-line review, please add seats to your subscription by visiting https://app.coderabbit.ai/login.If you believe this is a mistake and have available seats, please assign one to the pull request author through the subscription management page using the link above.

Comment @coderabbitai help to get the list of available commands and usage tips.

CopilotAI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR corrects an over-correction from #323: while error.code and error.param are still preserved verbatim from upstream (preserving #322's SDK retry-routing fix), error.type is now normalized to the DP-stable "upstream_error" token across all wires/paths, so upstream-private taxonomies (upstream_test_fixture, AccessDeniedException, authentication_error, etc.) no longer leak to customers. The translation tables in error_translate.rs are collapsed from (type, code) pairs to a single derived code.

Changes:

  • Hardcode kind = "upstream_error" in render_openai_envelope; introduce UPSTREAM_ERROR_TYPE constant.
  • Rename translate_*derive_*_code and return Option<String> only (the OpenAI-shape code).
  • Update unit and integration tests + their docstrings/names to assert the corrected contract.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated no comments.

FileDescription
crates/aisix-proxy/src/error_translate.rsDrop per-wire type derivation; emit DP-stable upstream_error and only derive OpenAI code. Update module doc and tests.
crates/aisix-proxy/src/lib.rsRename/retighten 3 integration tests to assert type == "upstream_error" while keeping code/param preservation assertions.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@moonming

moonming commented May 18, 2026

Copy link
Copy Markdown
MemberAuthor

Audit completed — verdict: merge, no HIGH/MEDIUM findings.

Two LOW findings:

Per CLAUDE.md §8 merge gate: ready when CI passes.

@moonming

Copy link
Copy Markdown
MemberAuthor

Follow-up on the audit's LOW-1: traced the path, not a real bug. BridgeError::UpstreamStatus is only constructed pre-stream (via map_http_error), and the stream itself only yields Transport/UpstreamDecode whose Display impls don't carry upstream body content. Closed #329 with the trace.

@moonming
moonming merged commit 30f70a2 into mainMay 18, 2026
12 checks passed
@moonming
moonming deleted the fix/error-type-normalize-327 branch May 18, 2026 05:30
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

bug(proxy): #322 over-corrected — error.type should normalize to DP taxonomy, not verbatim from upstream

2 participants

@moonming
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

fix(proxy): normalise upstream error.type to DP taxonomy, keep code/param verbatim (#327) - #328

Merged
moonming merged 1 commit into
mainfrom
fix/error-type-normalize-327
May 18, 2026
Merged

fix(proxy): normalise upstream error.type to DP taxonomy, keep code/param verbatim (#327)#328
moonming merged 1 commit into
mainfrom
fix/error-type-normalize-327

Conversation

@moonming

@moonmingmoonming commented May 18, 2026

Copy link
Copy Markdown
Member

Summary

Fixes#327. PR #323 over-corrected by preserving upstream's error.type verbatim — this broke the stable "upstream_error" contract that dp-upstream-error-live.spec.ts:152 already pins, and leaked upstream-private taxonomy (upstream_test_fixture, AccessDeniedException, authentication_error, etc.) to customers.

Contract correction

FieldSourceWhy
error.typeDP taxonomy: "upstream_error"Stable, customer-SDK-actionable, hides upstream zoo
error.messageupstream verbatim (4xx) / canned (5xx)Human-readable, already correct
error.codeupstream verbatim or derived per-wireSDK retry branches on this — preservation from #322 stays
error.paramupstream verbatimTells client which field caused the error — preservation from #322 stays

Customer SDKs branch on error.type == "upstream_error" for upstream-class detection and on error.code for granular retry routing.

Implementation

  • error_translate::render_openai_envelope: hardcodes kind to a new UPSTREAM_ERROR_TYPE constant ("upstream_error").
  • Translation tables collapse from (String, Option<String>) to Option<String> — the per-wire OpenAI type half is now uniformly "upstream_error", so the tables only carry the derived OpenAI string code.
  • 5xx, UpstreamWire::Unknown, and generic-envelope paths already emit "upstream_error" — this commit just makes the 4xx path consistent.

Reference impl divergence (CLAUDE.md §7)

The established gateway impls in this space normalise upstream taxonomy too — LiteLLM maps to a closed set of Python exception classes, Portkey emits its own stable type enum. Choosing a single DP token ("upstream_error") rather than a closed OpenAI-vocabulary set (invalid_request_error, rate_limit_exceeded, server_error) is more conservative — it sidesteps the question of how Bedrock's AccessDeniedException should map onto OpenAI's invalid_request_error vs authentication_error, since the customer just needs "upstream broke; check code for granularity."

Test plan

  • cargo check --workspace --all-targets clean
  • cargo clippy --workspace --all-targets clean
  • cargo fmt --check clean
  • cargo test --workspace — all tests green
  • 22 error_translate::tests updated: body.kind always "upstream_error", code assertions unchanged
  • 3 affected integration tests in aisix-proxy::tests updated and renamed to reflect the corrected contract:
    • upstream_openai_4xx_forwards_code_and_param_but_normalises_type_per_issue_327 (was ..._forwards_full_envelope_per_issue_322)
    • upstream_anthropic_400_normalises_type_to_upstream_error (was ..._passes_through_with_openai_envelope)
    • upstream_anthropic_rate_limit_derives_openai_rate_limit_exceeded_code (was ..._translates_to_openai_rate_limit_exceeded)
  • dp-upstream-error-live.spec.ts:152 should go back to green once :dev image is rebuilt with this PR merged.
  • AISIX-Cloud matrix spec tightening (adapter-openai-overrides-live.spec.ts:634 from upstream_test_fixture to upstream_error) is the issue author's follow-up.

Closes#327

Summary by CodeRabbit

  • Bug Fixes
    • Standardized upstream service error responses to use consistent error type formatting across all provider services, preventing provider-specific error taxonomy from being exposed to client applications.
    • Enhanced error code mapping to improve SDK compatibility and ensure proper retry routing behavior across integrated providers.

Review Change Stack

…/param verbatim (#327)
#323 over-corrected by preserving upstream's `error.type` verbatim,
which:
- broke `dp-upstream-error-live.spec.ts:152` (asserting the stable
`"upstream_error"` token) on every PR's e2e-playwright run from
the merge forward
- leaked upstream-private taxonomy to customers (mock-llm's
`upstream_test_fixture`, Bedrock's `AccessDeniedException`,
Anthropic's `authentication_error` etc.)
- defeated the gateway's role as a normalising layer that hides
the upstream zoo behind a stable, customer-SDK-actionable
taxonomy
This commit restores the DP-stable `error.type = "upstream_error"`
contract for upstream errors, while keeping the #322/#323
preservation of `error.code` and `error.param` — those are still
SDK-actionable and customer-facing-positive.
Final wire contract for `ProxyError::Bridge(UpstreamStatus { .. })`:
| Field | Source |
|----------------|---------------------------------|
| `error.type` | DP taxonomy: `"upstream_error"` |
| `error.message`| upstream verbatim (4xx) / canned `"upstream returned N"` (5xx) |
| `error.code` | upstream verbatim or derived per-wire (Anthropic/Bedrock/Vertex/Azure) |
| `error.param` | upstream verbatim |
Customer SDKs branch on `error.type == "upstream_error"` for
upstream-class detection and on `error.code` for granular retry
routing (`rate_limit_exceeded` vs `insufficient_quota` vs
`model_not_found` etc.).
Implementation:
* `error_translate::render_openai_envelope` hardcodes `kind` to the
new `UPSTREAM_ERROR_TYPE` constant. The translation tables
(Anthropic / Bedrock / Vertex / Azure) collapse from returning
`(String, Option<String>)` to just `Option<String>` — the type
half is now uniformly `"upstream_error"`, so the per-wire tables
only carry the derived OpenAI string code.
* Generic envelope (missing parsed view) also emits
`"upstream_error"` — unchanged from prior behaviour.
* 5xx and `UpstreamWire::Unknown` paths in
`render_bridge_upstream_envelope` already emit `"upstream_error"`;
this commit makes the 4xx path consistent.
Tests:
* All 22 `error_translate::tests` updated: `body.kind` assertions
pinned to `"upstream_error"`; code assertions unchanged.
* 3 integration tests in `aisix-proxy::tests` updated and renamed:
- `upstream_openai_4xx_forwards_full_envelope_per_issue_322` →
`upstream_openai_4xx_forwards_code_and_param_but_normalises_type_per_issue_327`
(asserts `upstream_test_fixture` does NOT leak)
- `upstream_anthropic_400_passes_through_with_openai_envelope` →
`upstream_anthropic_400_normalises_type_to_upstream_error`
- `upstream_anthropic_rate_limit_translates_to_openai_rate_limit_exceeded`
→
`upstream_anthropic_rate_limit_derives_openai_rate_limit_exceeded_code`
(the rate-limit `code` derivation is the customer-facing value;
the `type` is normalised)
Closes#327
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
CopilotAI review requested due to automatic review settings May 18, 2026 05:19
@coderabbitai

coderabbitaiBot commented May 18, 2026

Copy link
Copy Markdown
ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Free

Run ID: 443cdddd-c528-4f52-95ac-29328f6b5e15

📥 Commits

Reviewing files that changed from the base of the PR and between 1a744ee and 0fe40fc.

📒 Files selected for processing (2)
  • crates/aisix-proxy/src/error_translate.rs
  • crates/aisix-proxy/src/lib.rs

📝 Walkthrough

Walkthrough

This PR normalizes upstream error type handling in the proxy: error.type is always set to the DP-stable "upstream_error" token for upstream-originated 4xx errors, while error.code is intelligently derived via provider-specific helpers that map upstream taxonomies to OpenAI-compatible strings, with fallback preservation of upstream codes when derivation is unavailable.

Changes

Error type normalization and code derivation

Layer / File(s)Summary
Documentation and envelope rendering refactoring
crates/aisix-proxy/src/error_translate.rs
Module documentation updated to describe DP-stable error type normalization. The render_openai_envelope function refactored to always set kind to fixed "upstream_error" token and compute derived OpenAI code via per-upstream match logic that prefers derived codes for non-OpenAI wires while preserving upstream codes for OpenAI same-wire.
Error code derivation helpers
crates/aisix-proxy/src/error_translate.rs
Introduced UPSTREAM_ERROR_TYPE constant, updated generic envelope builder, and new derive_anthropic_code, derive_bedrock_code, derive_vertex_code, and derive_azure_code functions returning Option<String> for provider-specific OpenAI-compatible code mappings; Azure falls back to upstream code when derivation returns None.
Anthropic and Bedrock error handling tests
crates/aisix-proxy/src/error_translate.rs
Updated and added unit tests for Anthropic error kinds and Bedrock throttling to assert kind == "upstream_error" while validating derived code values (including None for unmapped kinds).
Bedrock, Vertex, and Azure error mapping tests
crates/aisix-proxy/src/error_translate.rs
Expanded tests for Bedrock quota/validation/access errors, Vertex gRPC status-code mappings, and Azure DeploymentNotFound to verify normalized kind and provider-specific derived code values.
Azure/OpenAI compatibility and same-wire preservation tests
crates/aisix-proxy/src/error_translate.rs
Tests for Azure content-policy violation derivation, Azure OpenAI-compatible code fallback behavior, OpenAI same-wire preservation of both code and param with normalized kind, and missing-parsed-message derived code assertion.
Cross-provider contract tests in lib.rs
crates/aisix-proxy/src/lib.rs
Integration-level tests verifying that OpenAI upstream 4xx and Anthropic 400/rate-limit errors enforce normalized error.type == "upstream_error" at the proxy boundary while preserving granular error.code and error.param for SDK retry branching and Anthropic→OpenAI code derivation.

🎯 3 (Moderate) | ⏱️ ~25 minutes


Note

🎁 Summarized by CodeRabbit Free

Your organization has reached its limit of developer seats under the Pro Plan. For new users, CodeRabbit will generate a high-level summary and a walkthrough for each pull request. For a comprehensive line-by-line review, please add seats to your subscription by visiting https://app.coderabbit.ai/login.If you believe this is a mistake and have available seats, please assign one to the pull request author through the subscription management page using the link above.

Comment @coderabbitai help to get the list of available commands and usage tips.

CopilotAI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR corrects an over-correction from #323: while error.code and error.param are still preserved verbatim from upstream (preserving #322's SDK retry-routing fix), error.type is now normalized to the DP-stable "upstream_error" token across all wires/paths, so upstream-private taxonomies (upstream_test_fixture, AccessDeniedException, authentication_error, etc.) no longer leak to customers. The translation tables in error_translate.rs are collapsed from (type, code) pairs to a single derived code.

Changes:

  • Hardcode kind = "upstream_error" in render_openai_envelope; introduce UPSTREAM_ERROR_TYPE constant.
  • Rename translate_*derive_*_code and return Option<String> only (the OpenAI-shape code).
  • Update unit and integration tests + their docstrings/names to assert the corrected contract.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated no comments.

FileDescription
crates/aisix-proxy/src/error_translate.rsDrop per-wire type derivation; emit DP-stable upstream_error and only derive OpenAI code. Update module doc and tests.
crates/aisix-proxy/src/lib.rsRename/retighten 3 integration tests to assert type == "upstream_error" while keeping code/param preservation assertions.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@moonming

moonming commented May 18, 2026

Copy link
Copy Markdown
MemberAuthor

Audit completed — verdict: merge, no HIGH/MEDIUM findings.

Two LOW findings:

Per CLAUDE.md §8 merge gate: ready when CI passes.

@moonming

Copy link
Copy Markdown
MemberAuthor

Follow-up on the audit's LOW-1: traced the path, not a real bug. BridgeError::UpstreamStatus is only constructed pre-stream (via map_http_error), and the stream itself only yields Transport/UpstreamDecode whose Display impls don't carry upstream body content. Closed #329 with the trace.

@moonming
moonming merged commit 30f70a2 into mainMay 18, 2026
12 checks passed
@moonming
moonming deleted the fix/error-type-normalize-327 branch May 18, 2026 05:30
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

bug(proxy): #322 over-corrected — error.type should normalize to DP taxonomy, not verbatim from upstream

2 participants

@moonming