Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
43 commits
Select commit Hold shift + click to select a range
a908992
Improve error handling and exception message presentation
Jul 9, 2026
eabf06c
refactor: improve error handling consistency and preserve original AP…
Jul 10, 2026
58e8f04
Fix CLI output handling to support both dict and object formats
Jul 13, 2026
1755866
Fix CLI output handling to support both dict and object formats
Jul 13, 2026
49f03c6
Merge branch 'main' into dev/errors
lzsweb Jul 13, 2026
fc1d296
fix: replace last remaining code='Unknown' with http_{status} in asyn…
Jul 13, 2026
2fe1241
refactor: unify error response handling and improve error code flexib…
Jul 15, 2026
032637d
feat: add centralized error registry and improve internal error handling
Jul 16, 2026
eafd429
feat: add centralized error registry and improve internal error handling
Jul 16, 2026
bee28b9
feat: add centralized error registry and improve internal error handling
Jul 16, 2026
f45a27b
refactor(cli): keep error codes in original camelCase format
Jul 17, 2026
67cc1ee
refactor: split _build_api_request to reduce statement count
Jul 17, 2026
5e201ac
fix: correct WebSocket URL scheme and fix lint errors
Jul 17, 2026
712d959
feat: enhance error handling for invalid URLs and authentication fail…
Jul 17, 2026
ffc401c
refactor: improve WebSocket error handling and cleanup unused code
Jul 22, 2026
5833cf5
fix: isolate AgenticRL internal error codes from public SDK API
Jul 28, 2026
3b1296d
feat: unify agentstudio error codes onto the centralized registry
foleydang Jul 28, 2026
dd5c016
refactor(agentic_rl): replace internal exception conversion with stan…
Jul 28, 2026
0c576c3
refactor(agentic_rl): replace custom exception conversion with standa…
Jul 28, 2026
565763d
refactor: unify error handling with centralized error registry
Jul 30, 2026
133c8e3
refactor: unify error handling with centralized error registry
Jul 30, 2026
2e7721e
feat(agentic-rl): add dedicated error definitions and align error cod…
Aug 4, 2026
bc88844
refactor: rename AGENTIC_RL error constants to CLIENT prefix and add …
Aug 5, 2026
1958e6e
refactor: rename AGENTIC_RL error constants to CLIENT prefix and add …
Aug 5, 2026
c49ef90
refactor: rename AGENTIC_RL error constants to CLIENT prefix and add …
Aug 5, 2026
f355c97
refactor: rename client error definitions and separate client errors …
Aug 5, 2026
aa178a3
refactor: rename SDK error codes from "sdk.*" to "agentic_rl.*" prefix
Aug 5, 2026
487ce64
fix: add SDK_ prefix to agentic_rl error definitions
Aug 6, 2026
2f3b45b
Merge branch 'main' into dev/errors
lzsweb Aug 6, 2026
d545ab9
fix: fill in AgenticRL error solutions and fix HTTP request bugs
Aug 6, 2026
72401b1
fix: fill in AgenticRL error solutions and fix HTTP request bugs
Aug 6, 2026
8d915c2
refactor: remove redundant ClientErrorDef class and unused gateway er…
Aug 7, 2026
9bac4d7
refactor: back agentstudio transport/stream error codes with the regi…
foleydang Aug 7, 2026
97af92c
refactor: classify agentstudio status errors by server code only
foleydang Aug 7, 2026
40b34b6
refactor: classify agentstudio status errors by server code only
foleydang Aug 7, 2026
2a974b5
Merge remote-tracking branch 'origin/dev/errors' into dev/errors
Aug 10, 2026
707b7a7
fix: declare missing dependencies for reinforcement module
Aug 19, 2026
c07e68d
Merge remote-tracking branch 'origin/main' into dev/errors
Aug 19, 2026
2628ad4
Merge remote-tracking branch 'origin/dev/errors' into dev/errors
Aug 19, 2026
0166804
refactor: simplify agentstudio exception handling and unify agentic…
Aug 19, 2026
afeda76
fix(agentstudio): use INTERNAL_ERROR constant for fallback error code
Aug 20, 2026
accf4a9
refactor(error_registry): remove hardcoded URLs and fix line length
Aug 20, 2026
2caf3b2
feat(error_registry): restore explicit URLs in solution messages
Aug 21, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 0 additions & 16 deletions dashscope/agentstudio/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,14 +35,6 @@
APIConnectionError,
APIStatusError,
APITimeoutError,
AuthenticationError,
ConflictError,
InternalServerError,
InvalidRequestError,
NotFoundError,
OverloadedError,
PermissionDeniedError,
RateLimitError,
StreamClosedError,
StreamError,
)
Expand Down Expand Up @@ -84,14 +76,6 @@
"APIConnectionError",
"APIStatusError",
"APITimeoutError",
"AuthenticationError",
"ConflictError",
"InternalServerError",
"InvalidRequestError",
"NotFoundError",
"OverloadedError",
"PermissionDeniedError",
"RateLimitError",
"StreamError",
"StreamClosedError",
# pagination
Expand Down
142 changes: 49 additions & 93 deletions dashscope/agentstudio/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,17 +10,28 @@
"request_id": "req_..."
}

The pre-release backend currently emits ``error_code``/``error_message``
instead of nested ``error.{code,message}``. We accept both shapes and
normalize to the documented form. The compatibility branch is marked
with ``# TODO(bma-fix)`` so we can remove it once the backend aligns.
Codes come from the server response and are preserved as-is. When no code
is present in the response, :func:`from_response` falls back to generic
``api_error`` rather than guessing from the status number. The raw payload
stays on ``.raw``.

Error classification is done via the ``code`` attribute rather than exception
subclasses, reducing maintenance burden and eliminating synchronization issues
with error registries.
"""

from __future__ import annotations

from typing import Any, Dict, Mapping, Optional
from typing import Any, Mapping, Optional

from dashscope.common.error import DashScopeException
from dashscope.common.error_registry import (
SDK_AGENTSTUDIO_API_CONNECTION_ERROR,
SDK_AGENTSTUDIO_API_TIMEOUT_ERROR,
SDK_AGENTSTUDIO_STREAM_CLOSED_ERROR,
SDK_AGENTSTUDIO_STREAM_ERROR,
INTERNAL_ERROR,
)


class AgentStudioError(DashScopeException):
Expand Down Expand Up @@ -75,13 +86,13 @@ def __repr__(self) -> str: # pragma: no cover - debug helper
class APIConnectionError(AgentStudioError):
"""Raised when the HTTP request fails before a response is read."""

code = "api_connection_error"
code = SDK_AGENTSTUDIO_API_CONNECTION_ERROR.name


class APITimeoutError(APIConnectionError):
"""Raised on connect / read timeouts."""

code = "api_timeout_error"
code = SDK_AGENTSTUDIO_API_TIMEOUT_ERROR.name


# ---------------------------------------------------------------------------
Expand All @@ -90,41 +101,14 @@ class APITimeoutError(APIConnectionError):


class APIStatusError(AgentStudioError):
"""Raised when the server returns a non-2xx status."""

code = "api_status_error"


class InvalidRequestError(APIStatusError):
code = "invalid_request_error"


class AuthenticationError(APIStatusError):
code = "authentication_error"


class PermissionDeniedError(APIStatusError):
code = "permission_denied_error"


class NotFoundError(APIStatusError):
code = "not_found_error"


class ConflictError(APIStatusError):
code = "conflict_error"


class RateLimitError(APIStatusError):
code = "rate_limit_error"


class OverloadedError(APIStatusError):
code = "overloaded_error"
"""Raised when the server returns a non-2xx status.

The specific error type is identified by the ``code`` attribute rather
than exception subclasses. The code is preserved from the server response.
When no code is present, falls back to ``api_error``.
"""

class InternalServerError(APIStatusError):
code = "api_error"
code = "api_status_error"


# ---------------------------------------------------------------------------
Expand All @@ -135,60 +119,40 @@ class InternalServerError(APIStatusError):
class StreamError(AgentStudioError):
"""Raised when an SSE stream encounters a fatal protocol error."""

code = "stream_error"
code = SDK_AGENTSTUDIO_STREAM_ERROR.name


class StreamClosedError(StreamError):
"""Raised when consumers attempt I/O on an already-closed stream."""

code = "stream_closed"
code = SDK_AGENTSTUDIO_STREAM_CLOSED_ERROR.name


# ---------------------------------------------------------------------------
# Dispatcher
# ---------------------------------------------------------------------------


_STATUS_TO_DEFAULT: Dict[int, type] = {
400: InvalidRequestError,
401: AuthenticationError,
403: PermissionDeniedError,
404: NotFoundError,
409: ConflictError,
429: RateLimitError,
500: InternalServerError,
502: InternalServerError,
503: OverloadedError,
504: InternalServerError,
}

_CODE_TO_CLASS: Dict[str, type] = {
"invalid_request_error": InvalidRequestError,
"authentication_error": AuthenticationError,
"permission_denied_error": PermissionDeniedError,
"not_found_error": NotFoundError,
"conflict_error": ConflictError,
"rate_limit_error": RateLimitError,
"overloaded_error": OverloadedError,
"api_error": InternalServerError,
}


def from_response(
*,
status_code: int,
body: Any,
headers: Optional[Mapping[str, str]] = None,
) -> AgentStudioError:
"""Build an :class:`AgentStudioError` instance from a HTTP response.
) -> APIStatusError:
"""Build an :class:`APIStatusError` instance from a HTTP response.

Accepts both the documented ``{type, error:{code,message}, request_id}``
shape and the pre-release ``{type, error:{error_code, error_message}}``
shape. Falls back to a Spring default ``{timestamp,status,error,path}``
when the body is not JSON-serializable.
Accepts the documented ``{type, error:{code,message}, request_id}`` shape
and the classic flat DashScope ``{code, message, request_id}`` envelope,
and falls back to a Spring default ``{timestamp,status,error,path}`` page.

The ``x-request-id`` response header is preferred over the body
``request_id`` field (server-generated IDs are more reliable for tracing).

The server's code is preserved as-is. Only when no code is present
does the function fall back to generic ``api_error``.

Error classification is done via the ``code`` attribute rather than
exception subclasses, simplifying the API and reducing maintenance.
"""

code: Optional[str] = None
Expand All @@ -202,40 +166,32 @@ def from_response(
if isinstance(body, Mapping):
# Body request_id as fallback (snake_case canonical).
if request_id is None:
request_id = body.get("request_id") or body.get(
"requestId",
) # TODO(bma-fix)
request_id = body.get("request_id")
err = body.get("error")
if isinstance(err, Mapping):
code = err.get("code") or err.get("error_code") # TODO(bma-fix)
message = err.get("message") or err.get(
"error_message",
) # TODO(bma-fix)
code = err.get("code")
message = err.get("message")
# Spring default fallback.
if (
message is None
and "error" in body
and isinstance(body["error"], str)
):
message = body["error"]
code = body.get("error") or "api_error"
# Flat DashScope envelope: code/message at the top level.
if code is None:
code = body.get("code")
if message is None:
message = body.get("message")

# Keep the server's code as-is; only fall back to api_error when missing.
if not code:
code = INTERNAL_ERROR.anthropic_error_code

if message is None:
message = f"HTTP {status_code}"
if code is None:
code = _STATUS_TO_DEFAULT.get( # type: ignore[attr-defined]
status_code,
APIStatusError,
).code

cls = (
_CODE_TO_CLASS.get(code)
or _STATUS_TO_DEFAULT.get(status_code)
or APIStatusError
)
return cls(

return APIStatusError(
message,
code=code,
request_id=request_id,
Expand Down
14 changes: 3 additions & 11 deletions dashscope/agentstudio/transport.py
Original file line number Diff line number Diff line change
Expand Up @@ -111,23 +111,15 @@ def unwrap(payload: Any) -> Tuple[Dict[str, Any], Optional[str]]:


def is_error_payload(payload: Any) -> bool:
"""Identify error bodies for both documented and pre-release shapes."""
"""Identify error bodies (``type == "error"`` or an ``error`` object)."""

if not isinstance(payload, dict):
return False
if payload.get("type") == "error":
return True
err = payload.get("error")
if isinstance(err, dict):
return any(
k in err
for k in (
"code",
"message",
"error_code",
"error_message",
)
)
return any(k in err for k in ("code", "message"))
return False


Expand Down Expand Up @@ -219,7 +211,7 @@ def build_headers(
"""Compose the canonical AgentStudio request headers."""

if not api_key:
raise exceptions.AuthenticationError(
raise exceptions.APIStatusError(
"api_key is required. Pass it via Client(api_key=...) or "
"the DASHSCOPE_API_KEY environment variable.",
code="authentication_error",
Expand Down
Loading
Loading