From fd3cf18a5dce1feb62015857bab1028eeacc84a2 Mon Sep 17 00:00:00 2001 From: kevin Date: Wed, 22 Jul 2026 16:57:27 +0800 Subject: [PATCH 1/7] fix: handle empty and non-JSON responses in DashScopeResult --- .../dashscope/common/DashScopeResult.java | 22 ++++++++++++++++++- .../runs/DefaultAssistantEventHandler.java | 2 +- 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/alibaba/dashscope/common/DashScopeResult.java b/src/main/java/com/alibaba/dashscope/common/DashScopeResult.java index ad71ae6d..8189b401 100644 --- a/src/main/java/com/alibaba/dashscope/common/DashScopeResult.java +++ b/src/main/java/com/alibaba/dashscope/common/DashScopeResult.java @@ -88,7 +88,27 @@ protected T fromResponse(Protocol protocol, NetworkResponse r this.output = response.getBinary(); } } else { - JsonObject jsonObject = JsonUtils.parse(response.getMessage()); + String message = response.getMessage(); + if (message == null || message.isEmpty()) { + this.output = null; + this.setStatusCode( + response.getHttpStatusCode() != null ? response.getHttpStatusCode() : 500); + this.setCode("EmptyResponse"); + this.setMessage("Response message is empty"); + return (T) this; + } + JsonObject jsonObject; + try { + jsonObject = JsonUtils.parse(message); + } catch (Exception e) { + // Non-JSON response (e.g., plain text from cancel async task) + this.output = null; + this.setStatusCode( + response.getHttpStatusCode() != null ? response.getHttpStatusCode() : 200); + this.setCode(""); + this.setMessage(message); + return (T) this; + } // Set HTTP status code if available if (response.getHttpStatusCode() != null) { this.setStatusCode(response.getHttpStatusCode()); diff --git a/src/main/java/com/alibaba/dashscope/threads/runs/DefaultAssistantEventHandler.java b/src/main/java/com/alibaba/dashscope/threads/runs/DefaultAssistantEventHandler.java index 80333e5d..d0bda500 100644 --- a/src/main/java/com/alibaba/dashscope/threads/runs/DefaultAssistantEventHandler.java +++ b/src/main/java/com/alibaba/dashscope/threads/runs/DefaultAssistantEventHandler.java @@ -188,7 +188,7 @@ public void onError(String errorMsg) { @Override public void onUnknown(String msg) { - System.out.println(msg); + // Ignored } @Override From c578bb69fc631e3e7defb84d5fed50087dc5a795 Mon Sep 17 00:00:00 2001 From: kevin Date: Wed, 22 Jul 2026 17:53:06 +0800 Subject: [PATCH 2/7] Bump version to 2.22.27 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 4b1b1274..f672ae5b 100644 --- a/pom.xml +++ b/pom.xml @@ -40,7 +40,7 @@ DashScope Java SDK com.alibaba dashscope-sdk-java - 2.22.26 + 2.22.27 8 From 9083d6984a3560909a04a4fe798a03779f07f7ff Mon Sep 17 00:00:00 2001 From: "zhansheng.lzs" Date: Fri, 24 Jul 2026 10:53:24 +0800 Subject: [PATCH 3/7] fix: guard all JsonUtils.parse calls in DashScopeResult against malformed/empty responses - Add null/empty message checks and try-catch for all JsonUtils.parse call sites (WebSocket, HTTP, isFlattenResult, encrypted branches) using PublicErrorDef.INTERNAL_ERROR - Introduce PublicErrorDef (copied from com.alibaba.dashscope:error:1.0.0) for standardized error codes and messages --- .../dashscope/common/DashScopeResult.java | 82 +++++++++++++++---- .../dashscope/common/PublicErrorDef.java | 66 +++++++++++++++ 2 files changed, 133 insertions(+), 15 deletions(-) create mode 100644 src/main/java/com/alibaba/dashscope/common/PublicErrorDef.java diff --git a/src/main/java/com/alibaba/dashscope/common/DashScopeResult.java b/src/main/java/com/alibaba/dashscope/common/DashScopeResult.java index 8189b401..a92df14d 100644 --- a/src/main/java/com/alibaba/dashscope/common/DashScopeResult.java +++ b/src/main/java/com/alibaba/dashscope/common/DashScopeResult.java @@ -16,7 +16,9 @@ import java.util.stream.Collectors; import lombok.Data; import lombok.EqualsAndHashCode; +import lombok.extern.slf4j.Slf4j; +@Slf4j @Data @EqualsAndHashCode(callSuper = true) public class DashScopeResult extends Result { @@ -34,7 +36,17 @@ protected T fromResponse(Protocol protocol, NetworkResponse r this.setHeaders(changeHeaders(response.getHeaders())); if (protocol == Protocol.WEBSOCKET) { if (response.getBinary() == null) { - JsonObject jsonObject = JsonUtils.parse(response.getMessage()); + JsonObject jsonObject; + try { + jsonObject = JsonUtils.parse(response.getMessage()); + } catch (Exception e) { + log.error("Failed to parse WebSocket message", e); + this.output = null; + this.setStatusCode(PublicErrorDef.INTERNAL_ERROR.getStatusCode()); + this.setCode(PublicErrorDef.INTERNAL_ERROR.getErrorCode()); + this.setMessage(PublicErrorDef.INTERNAL_ERROR.getErrorMsg()); + return (T) this; + } if (jsonObject.has(ApiKeywords.HEADER)) { JsonObject headers = jsonObject.get(ApiKeywords.HEADER).getAsJsonObject(); if (headers.has(ApiKeywords.TASKID)) { @@ -91,10 +103,9 @@ protected T fromResponse(Protocol protocol, NetworkResponse r String message = response.getMessage(); if (message == null || message.isEmpty()) { this.output = null; - this.setStatusCode( - response.getHttpStatusCode() != null ? response.getHttpStatusCode() : 500); - this.setCode("EmptyResponse"); - this.setMessage("Response message is empty"); + this.setStatusCode(PublicErrorDef.INTERNAL_ERROR.getStatusCode()); + this.setCode(PublicErrorDef.INTERNAL_ERROR.getErrorCode()); + this.setMessage(PublicErrorDef.INTERNAL_ERROR.getErrorMsg()); return (T) this; } JsonObject jsonObject; @@ -102,11 +113,11 @@ protected T fromResponse(Protocol protocol, NetworkResponse r jsonObject = JsonUtils.parse(message); } catch (Exception e) { // Non-JSON response (e.g., plain text from cancel async task) + log.warn("Failed to parse HTTP response as JSON: {}", message, e); this.output = null; - this.setStatusCode( - response.getHttpStatusCode() != null ? response.getHttpStatusCode() : 200); - this.setCode(""); - this.setMessage(message); + this.setStatusCode(PublicErrorDef.INTERNAL_ERROR.getStatusCode()); + this.setCode(PublicErrorDef.INTERNAL_ERROR.getErrorCode()); + this.setMessage(PublicErrorDef.INTERNAL_ERROR.getErrorMsg()); return (T) this; } // Set HTTP status code if available @@ -173,15 +184,29 @@ public T fromResponse( // flatten not support websocket. if (protocol == Protocol.WEBSOCKET) { if (response.getBinary() == null) { - JsonObject jsonObject = JsonUtils.parse(response.getMessage()); - this.output = jsonObject; + try { + this.output = JsonUtils.parse(response.getMessage()); + } catch (Exception e) { + log.error("Failed to parse WebSocket message", e); + this.output = null; + this.setStatusCode(PublicErrorDef.INTERNAL_ERROR.getStatusCode()); + this.setCode(PublicErrorDef.INTERNAL_ERROR.getErrorCode()); + this.setMessage(PublicErrorDef.INTERNAL_ERROR.getErrorMsg()); + } // convert to the result } else { this.output = response.getBinary(); } } else { // HTTP - JsonObject jsonObject = JsonUtils.parse(response.getMessage()); - this.output = jsonObject; + try { + this.output = JsonUtils.parse(response.getMessage()); + } catch (Exception e) { + log.error("Failed to parse HTTP response message", e); + this.output = null; + this.setStatusCode(PublicErrorDef.INTERNAL_ERROR.getStatusCode()); + this.setCode(PublicErrorDef.INTERNAL_ERROR.getErrorCode()); + this.setMessage(PublicErrorDef.INTERNAL_ERROR.getErrorMsg()); + } this.event = response.getEvent(); } return (T) this; @@ -202,7 +227,25 @@ public T fromResponse( if (response.getHttpStatusCode() != null) { this.setStatusCode(response.getHttpStatusCode()); } - JsonObject jsonObject = JsonUtils.parse(response.getMessage()); + String encryptedMessage = response.getMessage(); + if (encryptedMessage == null || encryptedMessage.isEmpty()) { + this.output = null; + this.setStatusCode(PublicErrorDef.INTERNAL_ERROR.getStatusCode()); + this.setCode(PublicErrorDef.INTERNAL_ERROR.getErrorCode()); + this.setMessage(PublicErrorDef.INTERNAL_ERROR.getErrorMsg()); + return (T) this; + } + JsonObject jsonObject; + try { + jsonObject = JsonUtils.parse(encryptedMessage); + } catch (Exception e) { + log.error("Failed to parse encrypted HTTP response message", e); + this.output = null; + this.setStatusCode(PublicErrorDef.INTERNAL_ERROR.getStatusCode()); + this.setCode(PublicErrorDef.INTERNAL_ERROR.getErrorCode()); + this.setMessage(PublicErrorDef.INTERNAL_ERROR.getErrorMsg()); + return (T) this; + } String encryptedOutput = jsonObject.get(ApiKeywords.OUTPUT).isJsonNull() ? null @@ -213,7 +256,16 @@ public T fromResponse( encryptedOutput, req.getEncryptionConfig().getAESEncryptKey(), req.getEncryptionConfig().getIv()); - this.output = JsonUtils.parse(plainOutput); + try { + this.output = JsonUtils.parse(plainOutput); + } catch (Exception e) { + log.error("Failed to parse decrypted output", e); + this.output = null; + this.setStatusCode(PublicErrorDef.INTERNAL_ERROR.getStatusCode()); + this.setCode(PublicErrorDef.INTERNAL_ERROR.getErrorCode()); + this.setMessage(PublicErrorDef.INTERNAL_ERROR.getErrorMsg()); + return (T) this; + } } else { this.output = null; } diff --git a/src/main/java/com/alibaba/dashscope/common/PublicErrorDef.java b/src/main/java/com/alibaba/dashscope/common/PublicErrorDef.java new file mode 100644 index 00000000..551ac4e1 --- /dev/null +++ b/src/main/java/com/alibaba/dashscope/common/PublicErrorDef.java @@ -0,0 +1,66 @@ +// Copied from com.alibaba.dashscope:error:1.0.0 +package com.alibaba.dashscope.common; + +// Auto-generated by tools/generate.py -- DO NOT EDIT. +import java.util.Map; + +/** Customer-facing error definitions. */ +public enum PublicErrorDef { + INVALID_REQUEST(400, "BadRequestError", + "The request is invalid. Please check the request and try again.", "invalid_request_error"), + MISSING_PARAMETER(400, "BadRequestError", + "Missing required parameter: {parameter}.", "invalid_request_error"), + CONTENT_POLICY_VIOLATION(400, "BadRequestError", + "The request was rejected by content policy.", "invalid_request_error"), + INVALID_URL(400, "BadRequestError", + "The provided URL is invalid or cannot be accessed.", "invalid_request_error"), + INVALID_FILE(400, "BadRequestError", + "The provided file is invalid.", "invalid_request_error"), + AUTH_FAILED(401, "AuthenticationError", + "Authentication failed. Please provide valid authentication credentials.", "authentication_error"), + INVALID_API_KEY(401, "AuthenticationError", + "Incorrect API key provided.", "authentication_error"), + PERMISSION_DENIED(403, "PermissionDeniedError", + "You do not have permission to access this resource.", "permission_error"), + RESOURCE_NOT_FOUND(404, "NotFoundError", + "The requested resource was not found: {resource}.", "not_found_error"), + REQUEST_TOO_LARGE(413, "RequestTooLargeError", + "The request exceeds the maximum allowed size of {limit}.", "request_too_large"), + RATE_LIMIT_EXCEEDED(429, "RateLimitError", + "Rate limit exceeded. Please slow down your requests.", "rate_limit_error"), + CONCURRENCY_LIMIT_EXCEEDED(429, "RateLimitError", + "Too many concurrent requests. Please reduce concurrency and try again.", "rate_limit_error"), + INSUFFICIENT_QUOTA(429, "RateLimitError", + "You exceeded your current quota. Please check your plan and billing details.", "billing_error"), + INTERNAL_ERROR(500, "InternalServerError", + "The server encountered an internal error. Please try again later.", "api_error"), + SERVICE_UNAVAILABLE(503, "ServiceUnavailableError", + "The service is temporarily unavailable. Please try again later.", "overloaded_error"), + REQUEST_TIMEOUT(504, "GatewayTimeoutError", + "The request timed out. Please try again later.", "timeout_error"); + + private final int statusCode; + private final String errorCode; + private final String errorMsg; + private final String anthropicErrorCode; + + PublicErrorDef(int statusCode, String errorCode, String errorMsg, String anthropicErrorCode) { + this.statusCode = statusCode; + this.errorCode = errorCode; + this.errorMsg = errorMsg; + this.anthropicErrorCode = anthropicErrorCode; + } + + public int getStatusCode() { return statusCode; } + public String getErrorCode() { return errorCode; } + public String getErrorMsg() { return errorMsg; } + public String getAnthropicErrorCode() { return anthropicErrorCode; } + + public String formatMsg(Map vars) { + String msg = errorMsg; + for (Map.Entry entry : vars.entrySet()) { + msg = msg.replace("{" + entry.getKey() + "}", entry.getValue()); + } + return msg; + } +} From 2cd643238618f479b9e7d10d54cc8a55fadabde5 Mon Sep 17 00:00:00 2001 From: "zhansheng.lzs" Date: Fri, 24 Jul 2026 11:10:26 +0800 Subject: [PATCH 4/7] refactor: extract private helpers in DashScopeResult to reduce fromResponse redundancy Extracted setInternalError(), parseJson(), populateFromHttpJson(), and fromWebSocketMessage() to eliminate repeated error-handling and JSON field extraction across three fromResponse() overloads. --- .../dashscope/common/DashScopeResult.java | 328 ++++++------------ 1 file changed, 109 insertions(+), 219 deletions(-) diff --git a/src/main/java/com/alibaba/dashscope/common/DashScopeResult.java b/src/main/java/com/alibaba/dashscope/common/DashScopeResult.java index a92df14d..fd180851 100644 --- a/src/main/java/com/alibaba/dashscope/common/DashScopeResult.java +++ b/src/main/java/com/alibaba/dashscope/common/DashScopeResult.java @@ -36,139 +36,23 @@ protected T fromResponse(Protocol protocol, NetworkResponse r this.setHeaders(changeHeaders(response.getHeaders())); if (protocol == Protocol.WEBSOCKET) { if (response.getBinary() == null) { - JsonObject jsonObject; - try { - jsonObject = JsonUtils.parse(response.getMessage()); - } catch (Exception e) { - log.error("Failed to parse WebSocket message", e); - this.output = null; - this.setStatusCode(PublicErrorDef.INTERNAL_ERROR.getStatusCode()); - this.setCode(PublicErrorDef.INTERNAL_ERROR.getErrorCode()); - this.setMessage(PublicErrorDef.INTERNAL_ERROR.getErrorMsg()); - return (T) this; - } - if (jsonObject.has(ApiKeywords.HEADER)) { - JsonObject headers = jsonObject.get(ApiKeywords.HEADER).getAsJsonObject(); - if (headers.has(ApiKeywords.TASKID)) { - this.setRequestId(headers.get(ApiKeywords.TASKID).getAsString()); - } - // Extract status_code, code and message from header - if (headers.has(ApiKeywords.STATUS_CODE)) { - this.setStatusCode( - headers.get(ApiKeywords.STATUS_CODE).isJsonNull() - ? null - : headers.get(ApiKeywords.STATUS_CODE).getAsInt()); - } else { - // Set default status code - this.setStatusCode(200); - } - if (headers.has(ApiKeywords.ERROR_CODE)) { - this.setCode( - headers.get(ApiKeywords.ERROR_CODE).isJsonNull() - ? "" - : headers.get(ApiKeywords.ERROR_CODE).getAsString()); - } else { - // Set default empty string for successful responses - this.setCode(""); - } - if (headers.has(ApiKeywords.ERROR_MESSAGE)) { - this.setMessage( - headers.get(ApiKeywords.ERROR_MESSAGE).isJsonNull() - ? "" - : headers.get(ApiKeywords.ERROR_MESSAGE).getAsString()); - } else { - // Set default empty string for successful responses - this.setMessage(""); - } - } - if (jsonObject.has(ApiKeywords.PAYLOAD)) { - JsonObject payload = jsonObject.getAsJsonObject(ApiKeywords.PAYLOAD); - if (payload.has(ApiKeywords.OUTPUT)) { - this.output = - payload.get(ApiKeywords.OUTPUT).isJsonNull() - ? null - : payload.get(ApiKeywords.OUTPUT); - } - if (payload.has(ApiKeywords.USAGE)) { - this.setUsage( - payload.get(ApiKeywords.USAGE).isJsonNull() - ? null - : payload.get(ApiKeywords.USAGE)); - } - } + fromWebSocketMessage(response.getMessage()); } else { this.output = response.getBinary(); } } else { String message = response.getMessage(); if (message == null || message.isEmpty()) { - this.output = null; - this.setStatusCode(PublicErrorDef.INTERNAL_ERROR.getStatusCode()); - this.setCode(PublicErrorDef.INTERNAL_ERROR.getErrorCode()); - this.setMessage(PublicErrorDef.INTERNAL_ERROR.getErrorMsg()); + log.warn("HTTP response message is null or empty, httpStatusCode: {}", response.getHttpStatusCode()); + setInternalError(); return (T) this; } - JsonObject jsonObject; - try { - jsonObject = JsonUtils.parse(message); - } catch (Exception e) { - // Non-JSON response (e.g., plain text from cancel async task) - log.warn("Failed to parse HTTP response as JSON: {}", message, e); - this.output = null; - this.setStatusCode(PublicErrorDef.INTERNAL_ERROR.getStatusCode()); - this.setCode(PublicErrorDef.INTERNAL_ERROR.getErrorCode()); - this.setMessage(PublicErrorDef.INTERNAL_ERROR.getErrorMsg()); - return (T) this; - } - // Set HTTP status code if available + JsonObject jsonObject = parseJson(message, "Failed to parse HTTP response as JSON: {}"); + if (jsonObject == null) return (T) this; if (response.getHttpStatusCode() != null) { this.setStatusCode(response.getHttpStatusCode()); } - if (jsonObject.has(ApiKeywords.OUTPUT)) { - this.output = - jsonObject.get(ApiKeywords.OUTPUT).isJsonNull() - ? null - : jsonObject.get(ApiKeywords.OUTPUT).getAsJsonObject(); - } - if (jsonObject.has(ApiKeywords.USAGE)) { - this.setUsage( - jsonObject.get(ApiKeywords.USAGE).isJsonNull() - ? null - : jsonObject.get(ApiKeywords.USAGE).getAsJsonObject()); - } - if (jsonObject.has(ApiKeywords.REQUEST_ID)) { - this.setRequestId(jsonObject.get(ApiKeywords.REQUEST_ID).getAsString()); - } - if (jsonObject.has(ApiKeywords.STATUS_CODE)) { - this.setStatusCode( - jsonObject.get(ApiKeywords.STATUS_CODE).isJsonNull() - ? null - : jsonObject.get(ApiKeywords.STATUS_CODE).getAsInt()); - } - if (jsonObject.has(ApiKeywords.CODE)) { - this.setCode( - jsonObject.get(ApiKeywords.CODE).isJsonNull() - ? "" - : jsonObject.get(ApiKeywords.CODE).getAsString()); - } else { - // Set default empty string for successful responses - this.setCode(""); - } - if (jsonObject.has(ApiKeywords.MESSAGE)) { - this.setMessage( - jsonObject.get(ApiKeywords.MESSAGE).isJsonNull() - ? "" - : jsonObject.get(ApiKeywords.MESSAGE).getAsString()); - } else { - // Set default empty string for successful responses - this.setMessage(""); - } - if (jsonObject.has(ApiKeywords.DATA)) { - if (jsonObject.has(ApiKeywords.REQUEST_ID)) { - jsonObject.remove(ApiKeywords.REQUEST_ID); - } - this.output = jsonObject; - } + populateFromHttpJson(jsonObject); } return (T) this; } @@ -179,38 +63,19 @@ public T fromResponse( Protocol protocol, NetworkResponse response, boolean isFlattenResult) throws ApiException { if (!isFlattenResult) { return fromResponse(protocol, response); - } else { - this.setHeaders(changeHeaders(response.getHeaders())); - // flatten not support websocket. - if (protocol == Protocol.WEBSOCKET) { - if (response.getBinary() == null) { - try { - this.output = JsonUtils.parse(response.getMessage()); - } catch (Exception e) { - log.error("Failed to parse WebSocket message", e); - this.output = null; - this.setStatusCode(PublicErrorDef.INTERNAL_ERROR.getStatusCode()); - this.setCode(PublicErrorDef.INTERNAL_ERROR.getErrorCode()); - this.setMessage(PublicErrorDef.INTERNAL_ERROR.getErrorMsg()); - } - // convert to the result - } else { - this.output = response.getBinary(); - } - } else { // HTTP - try { - this.output = JsonUtils.parse(response.getMessage()); - } catch (Exception e) { - log.error("Failed to parse HTTP response message", e); - this.output = null; - this.setStatusCode(PublicErrorDef.INTERNAL_ERROR.getStatusCode()); - this.setCode(PublicErrorDef.INTERNAL_ERROR.getErrorCode()); - this.setMessage(PublicErrorDef.INTERNAL_ERROR.getErrorMsg()); - } - this.event = response.getEvent(); + } + this.setHeaders(changeHeaders(response.getHeaders())); + if (protocol == Protocol.WEBSOCKET) { + if (response.getBinary() == null) { + this.output = parseJson(response.getMessage(), "Failed to parse WebSocket message"); + } else { + this.output = response.getBinary(); } - return (T) this; + } else { + this.output = parseJson(response.getMessage(), "Failed to parse HTTP response message"); + this.event = response.getEvent(); } + return (T) this; } @Override @@ -219,33 +84,20 @@ public T fromResponse( Protocol protocol, NetworkResponse response, boolean isFlattenResult, HalfDuplexRequest req) throws ApiException { this.setHeaders(changeHeaders(response.getHeaders())); - // check it's encrypted output if ((response.getHeaders().containsKey("X-DashScope-OutputEncrypted".toLowerCase()) || req.isEncryptRequest()) && protocol == Protocol.HTTP) { - // Set HTTP status code if available if (response.getHttpStatusCode() != null) { this.setStatusCode(response.getHttpStatusCode()); } String encryptedMessage = response.getMessage(); if (encryptedMessage == null || encryptedMessage.isEmpty()) { - this.output = null; - this.setStatusCode(PublicErrorDef.INTERNAL_ERROR.getStatusCode()); - this.setCode(PublicErrorDef.INTERNAL_ERROR.getErrorCode()); - this.setMessage(PublicErrorDef.INTERNAL_ERROR.getErrorMsg()); - return (T) this; - } - JsonObject jsonObject; - try { - jsonObject = JsonUtils.parse(encryptedMessage); - } catch (Exception e) { - log.error("Failed to parse encrypted HTTP response message", e); - this.output = null; - this.setStatusCode(PublicErrorDef.INTERNAL_ERROR.getStatusCode()); - this.setCode(PublicErrorDef.INTERNAL_ERROR.getErrorCode()); - this.setMessage(PublicErrorDef.INTERNAL_ERROR.getErrorMsg()); + log.warn("Encrypted HTTP response message is null or empty, httpStatusCode: {}", response.getHttpStatusCode()); + setInternalError(); return (T) this; } + JsonObject jsonObject = parseJson(encryptedMessage, "Failed to parse encrypted HTTP response message"); + if (jsonObject == null) return (T) this; String encryptedOutput = jsonObject.get(ApiKeywords.OUTPUT).isJsonNull() ? null @@ -256,62 +108,100 @@ public T fromResponse( encryptedOutput, req.getEncryptionConfig().getAESEncryptKey(), req.getEncryptionConfig().getIv()); - try { - this.output = JsonUtils.parse(plainOutput); - } catch (Exception e) { - log.error("Failed to parse decrypted output", e); - this.output = null; - this.setStatusCode(PublicErrorDef.INTERNAL_ERROR.getStatusCode()); - this.setCode(PublicErrorDef.INTERNAL_ERROR.getErrorCode()); - this.setMessage(PublicErrorDef.INTERNAL_ERROR.getErrorMsg()); - return (T) this; - } - } else { - this.output = null; - } - if (jsonObject.has(ApiKeywords.USAGE)) { - this.setUsage( - jsonObject.get(ApiKeywords.USAGE).isJsonNull() - ? null - : jsonObject.get(ApiKeywords.USAGE).getAsJsonObject()); - } - if (jsonObject.has(ApiKeywords.REQUEST_ID)) { - this.setRequestId(jsonObject.get(ApiKeywords.REQUEST_ID).getAsString()); - } - if (jsonObject.has(ApiKeywords.STATUS_CODE)) { - this.setStatusCode( - jsonObject.get(ApiKeywords.STATUS_CODE).isJsonNull() - ? null - : jsonObject.get(ApiKeywords.STATUS_CODE).getAsInt()); - } - if (jsonObject.has(ApiKeywords.CODE)) { - this.setCode( - jsonObject.get(ApiKeywords.CODE).isJsonNull() - ? "" - : jsonObject.get(ApiKeywords.CODE).getAsString()); - } else { - // Set default empty string for successful responses - this.setCode(""); - } - if (jsonObject.has(ApiKeywords.MESSAGE)) { - this.setMessage( - jsonObject.get(ApiKeywords.MESSAGE).isJsonNull() - ? "" - : jsonObject.get(ApiKeywords.MESSAGE).getAsString()); - } else { - // Set default empty string for successful responses - this.setMessage(""); - } - if (jsonObject.has(ApiKeywords.DATA)) { - if (jsonObject.has(ApiKeywords.REQUEST_ID)) { - jsonObject.remove(ApiKeywords.REQUEST_ID); - } + this.output = parseJson(plainOutput, "Failed to parse decrypted output"); + if (this.output == null) return (T) this; } + populateFromHttpJson(jsonObject); return (T) this; } return fromResponse(protocol, response, isFlattenResult); } + private void fromWebSocketMessage(String message) { + JsonObject jsonObject = parseJson(message, "Failed to parse WebSocket message"); + if (jsonObject == null) return; + if (jsonObject.has(ApiKeywords.HEADER)) { + JsonObject headers = jsonObject.get(ApiKeywords.HEADER).getAsJsonObject(); + if (headers.has(ApiKeywords.TASKID)) { + this.setRequestId(headers.get(ApiKeywords.TASKID).getAsString()); + } + this.setStatusCode( + headers.has(ApiKeywords.STATUS_CODE) && !headers.get(ApiKeywords.STATUS_CODE).isJsonNull() + ? headers.get(ApiKeywords.STATUS_CODE).getAsInt() + : 200); + this.setCode( + headers.has(ApiKeywords.ERROR_CODE) && !headers.get(ApiKeywords.ERROR_CODE).isJsonNull() + ? headers.get(ApiKeywords.ERROR_CODE).getAsString() + : ""); + this.setMessage( + headers.has(ApiKeywords.ERROR_MESSAGE) && !headers.get(ApiKeywords.ERROR_MESSAGE).isJsonNull() + ? headers.get(ApiKeywords.ERROR_MESSAGE).getAsString() + : ""); + } + if (jsonObject.has(ApiKeywords.PAYLOAD)) { + JsonObject payload = jsonObject.getAsJsonObject(ApiKeywords.PAYLOAD); + if (payload.has(ApiKeywords.OUTPUT)) { + this.output = payload.get(ApiKeywords.OUTPUT).isJsonNull() ? null : payload.get(ApiKeywords.OUTPUT); + } + if (payload.has(ApiKeywords.USAGE)) { + this.setUsage(payload.get(ApiKeywords.USAGE).isJsonNull() ? null : payload.get(ApiKeywords.USAGE)); + } + } + } + + private void populateFromHttpJson(JsonObject jsonObject) { + if (jsonObject.has(ApiKeywords.OUTPUT)) { + this.output = + jsonObject.get(ApiKeywords.OUTPUT).isJsonNull() + ? null + : jsonObject.get(ApiKeywords.OUTPUT).getAsJsonObject(); + } + if (jsonObject.has(ApiKeywords.USAGE)) { + this.setUsage( + jsonObject.get(ApiKeywords.USAGE).isJsonNull() + ? null + : jsonObject.get(ApiKeywords.USAGE).getAsJsonObject()); + } + if (jsonObject.has(ApiKeywords.REQUEST_ID)) { + this.setRequestId(jsonObject.get(ApiKeywords.REQUEST_ID).getAsString()); + } + if (jsonObject.has(ApiKeywords.STATUS_CODE)) { + this.setStatusCode( + jsonObject.get(ApiKeywords.STATUS_CODE).isJsonNull() + ? null + : jsonObject.get(ApiKeywords.STATUS_CODE).getAsInt()); + } + this.setCode( + jsonObject.has(ApiKeywords.CODE) && !jsonObject.get(ApiKeywords.CODE).isJsonNull() + ? jsonObject.get(ApiKeywords.CODE).getAsString() + : ""); + this.setMessage( + jsonObject.has(ApiKeywords.MESSAGE) && !jsonObject.get(ApiKeywords.MESSAGE).isJsonNull() + ? jsonObject.get(ApiKeywords.MESSAGE).getAsString() + : ""); + if (jsonObject.has(ApiKeywords.DATA)) { + jsonObject.remove(ApiKeywords.REQUEST_ID); + this.output = jsonObject; + } + } + + private JsonObject parseJson(String raw, String logMsg) { + try { + return JsonUtils.parse(raw); + } catch (Exception e) { + log.error(logMsg, e); + setInternalError(); + return null; + } + } + + private void setInternalError() { + this.output = null; + this.setStatusCode(PublicErrorDef.INTERNAL_ERROR.getStatusCode()); + this.setCode(PublicErrorDef.INTERNAL_ERROR.getErrorCode()); + this.setMessage(PublicErrorDef.INTERNAL_ERROR.getErrorMsg()); + } + private Map changeHeaders(Map> headers) { if (headers == null || headers.isEmpty()) { return null; From f4253bf26d1693961de9ee192604180865908b10 Mon Sep 17 00:00:00 2001 From: "zhansheng.lzs" Date: Fri, 24 Jul 2026 11:14:45 +0800 Subject: [PATCH 5/7] style: apply lint formatting to DashScopeResult and PublicErrorDef --- .../dashscope/common/DashScopeResult.java | 20 ++- .../dashscope/common/PublicErrorDef.java | 150 ++++++++++++------ 2 files changed, 112 insertions(+), 58 deletions(-) diff --git a/src/main/java/com/alibaba/dashscope/common/DashScopeResult.java b/src/main/java/com/alibaba/dashscope/common/DashScopeResult.java index fd180851..45f3f86e 100644 --- a/src/main/java/com/alibaba/dashscope/common/DashScopeResult.java +++ b/src/main/java/com/alibaba/dashscope/common/DashScopeResult.java @@ -43,7 +43,9 @@ protected T fromResponse(Protocol protocol, NetworkResponse r } else { String message = response.getMessage(); if (message == null || message.isEmpty()) { - log.warn("HTTP response message is null or empty, httpStatusCode: {}", response.getHttpStatusCode()); + log.warn( + "HTTP response message is null or empty, httpStatusCode: {}", + response.getHttpStatusCode()); setInternalError(); return (T) this; } @@ -92,11 +94,14 @@ public T fromResponse( } String encryptedMessage = response.getMessage(); if (encryptedMessage == null || encryptedMessage.isEmpty()) { - log.warn("Encrypted HTTP response message is null or empty, httpStatusCode: {}", response.getHttpStatusCode()); + log.warn( + "Encrypted HTTP response message is null or empty, httpStatusCode: {}", + response.getHttpStatusCode()); setInternalError(); return (T) this; } - JsonObject jsonObject = parseJson(encryptedMessage, "Failed to parse encrypted HTTP response message"); + JsonObject jsonObject = + parseJson(encryptedMessage, "Failed to parse encrypted HTTP response message"); if (jsonObject == null) return (T) this; String encryptedOutput = jsonObject.get(ApiKeywords.OUTPUT).isJsonNull() @@ -134,17 +139,20 @@ private void fromWebSocketMessage(String message) { ? headers.get(ApiKeywords.ERROR_CODE).getAsString() : ""); this.setMessage( - headers.has(ApiKeywords.ERROR_MESSAGE) && !headers.get(ApiKeywords.ERROR_MESSAGE).isJsonNull() + headers.has(ApiKeywords.ERROR_MESSAGE) + && !headers.get(ApiKeywords.ERROR_MESSAGE).isJsonNull() ? headers.get(ApiKeywords.ERROR_MESSAGE).getAsString() : ""); } if (jsonObject.has(ApiKeywords.PAYLOAD)) { JsonObject payload = jsonObject.getAsJsonObject(ApiKeywords.PAYLOAD); if (payload.has(ApiKeywords.OUTPUT)) { - this.output = payload.get(ApiKeywords.OUTPUT).isJsonNull() ? null : payload.get(ApiKeywords.OUTPUT); + this.output = + payload.get(ApiKeywords.OUTPUT).isJsonNull() ? null : payload.get(ApiKeywords.OUTPUT); } if (payload.has(ApiKeywords.USAGE)) { - this.setUsage(payload.get(ApiKeywords.USAGE).isJsonNull() ? null : payload.get(ApiKeywords.USAGE)); + this.setUsage( + payload.get(ApiKeywords.USAGE).isJsonNull() ? null : payload.get(ApiKeywords.USAGE)); } } } diff --git a/src/main/java/com/alibaba/dashscope/common/PublicErrorDef.java b/src/main/java/com/alibaba/dashscope/common/PublicErrorDef.java index 551ac4e1..e8be4fa5 100644 --- a/src/main/java/com/alibaba/dashscope/common/PublicErrorDef.java +++ b/src/main/java/com/alibaba/dashscope/common/PublicErrorDef.java @@ -6,61 +6,107 @@ /** Customer-facing error definitions. */ public enum PublicErrorDef { - INVALID_REQUEST(400, "BadRequestError", - "The request is invalid. Please check the request and try again.", "invalid_request_error"), - MISSING_PARAMETER(400, "BadRequestError", - "Missing required parameter: {parameter}.", "invalid_request_error"), - CONTENT_POLICY_VIOLATION(400, "BadRequestError", - "The request was rejected by content policy.", "invalid_request_error"), - INVALID_URL(400, "BadRequestError", - "The provided URL is invalid or cannot be accessed.", "invalid_request_error"), - INVALID_FILE(400, "BadRequestError", - "The provided file is invalid.", "invalid_request_error"), - AUTH_FAILED(401, "AuthenticationError", - "Authentication failed. Please provide valid authentication credentials.", "authentication_error"), - INVALID_API_KEY(401, "AuthenticationError", - "Incorrect API key provided.", "authentication_error"), - PERMISSION_DENIED(403, "PermissionDeniedError", - "You do not have permission to access this resource.", "permission_error"), - RESOURCE_NOT_FOUND(404, "NotFoundError", - "The requested resource was not found: {resource}.", "not_found_error"), - REQUEST_TOO_LARGE(413, "RequestTooLargeError", - "The request exceeds the maximum allowed size of {limit}.", "request_too_large"), - RATE_LIMIT_EXCEEDED(429, "RateLimitError", - "Rate limit exceeded. Please slow down your requests.", "rate_limit_error"), - CONCURRENCY_LIMIT_EXCEEDED(429, "RateLimitError", - "Too many concurrent requests. Please reduce concurrency and try again.", "rate_limit_error"), - INSUFFICIENT_QUOTA(429, "RateLimitError", - "You exceeded your current quota. Please check your plan and billing details.", "billing_error"), - INTERNAL_ERROR(500, "InternalServerError", - "The server encountered an internal error. Please try again later.", "api_error"), - SERVICE_UNAVAILABLE(503, "ServiceUnavailableError", - "The service is temporarily unavailable. Please try again later.", "overloaded_error"), - REQUEST_TIMEOUT(504, "GatewayTimeoutError", - "The request timed out. Please try again later.", "timeout_error"); + INVALID_REQUEST( + 400, + "BadRequestError", + "The request is invalid. Please check the request and try again.", + "invalid_request_error"), + MISSING_PARAMETER( + 400, "BadRequestError", "Missing required parameter: {parameter}.", "invalid_request_error"), + CONTENT_POLICY_VIOLATION( + 400, + "BadRequestError", + "The request was rejected by content policy.", + "invalid_request_error"), + INVALID_URL( + 400, + "BadRequestError", + "The provided URL is invalid or cannot be accessed.", + "invalid_request_error"), + INVALID_FILE(400, "BadRequestError", "The provided file is invalid.", "invalid_request_error"), + AUTH_FAILED( + 401, + "AuthenticationError", + "Authentication failed. Please provide valid authentication credentials.", + "authentication_error"), + INVALID_API_KEY( + 401, "AuthenticationError", "Incorrect API key provided.", "authentication_error"), + PERMISSION_DENIED( + 403, + "PermissionDeniedError", + "You do not have permission to access this resource.", + "permission_error"), + RESOURCE_NOT_FOUND( + 404, "NotFoundError", "The requested resource was not found: {resource}.", "not_found_error"), + REQUEST_TOO_LARGE( + 413, + "RequestTooLargeError", + "The request exceeds the maximum allowed size of {limit}.", + "request_too_large"), + RATE_LIMIT_EXCEEDED( + 429, + "RateLimitError", + "Rate limit exceeded. Please slow down your requests.", + "rate_limit_error"), + CONCURRENCY_LIMIT_EXCEEDED( + 429, + "RateLimitError", + "Too many concurrent requests. Please reduce concurrency and try again.", + "rate_limit_error"), + INSUFFICIENT_QUOTA( + 429, + "RateLimitError", + "You exceeded your current quota. Please check your plan and billing details.", + "billing_error"), + INTERNAL_ERROR( + 500, + "InternalServerError", + "The server encountered an internal error. Please try again later.", + "api_error"), + SERVICE_UNAVAILABLE( + 503, + "ServiceUnavailableError", + "The service is temporarily unavailable. Please try again later.", + "overloaded_error"), + REQUEST_TIMEOUT( + 504, + "GatewayTimeoutError", + "The request timed out. Please try again later.", + "timeout_error"); - private final int statusCode; - private final String errorCode; - private final String errorMsg; - private final String anthropicErrorCode; + private final int statusCode; + private final String errorCode; + private final String errorMsg; + private final String anthropicErrorCode; - PublicErrorDef(int statusCode, String errorCode, String errorMsg, String anthropicErrorCode) { - this.statusCode = statusCode; - this.errorCode = errorCode; - this.errorMsg = errorMsg; - this.anthropicErrorCode = anthropicErrorCode; - } + PublicErrorDef(int statusCode, String errorCode, String errorMsg, String anthropicErrorCode) { + this.statusCode = statusCode; + this.errorCode = errorCode; + this.errorMsg = errorMsg; + this.anthropicErrorCode = anthropicErrorCode; + } + + public int getStatusCode() { + return statusCode; + } + + public String getErrorCode() { + return errorCode; + } + + public String getErrorMsg() { + return errorMsg; + } - public int getStatusCode() { return statusCode; } - public String getErrorCode() { return errorCode; } - public String getErrorMsg() { return errorMsg; } - public String getAnthropicErrorCode() { return anthropicErrorCode; } + public String getAnthropicErrorCode() { + return anthropicErrorCode; + } - public String formatMsg(Map vars) { - String msg = errorMsg; - for (Map.Entry entry : vars.entrySet()) { - msg = msg.replace("{" + entry.getKey() + "}", entry.getValue()); - } - return msg; + public String formatMsg(Map vars) { + String msg = errorMsg; + for (Map.Entry entry : vars.entrySet()) { + msg = msg.replace("{" + entry.getKey() + "}", entry.getValue()); } + return msg; + } } From 99a1111ffce4ebfc10c071a41f7e4248e1cbe689 Mon Sep 17 00:00:00 2001 From: kevin Date: Fri, 24 Jul 2026 15:52:58 +0800 Subject: [PATCH 6/7] fix: handle null/empty WebSocket message and refactor JSON field processing --- .../dashscope/common/DashScopeResult.java | 59 ++++++++++++++----- 1 file changed, 44 insertions(+), 15 deletions(-) diff --git a/src/main/java/com/alibaba/dashscope/common/DashScopeResult.java b/src/main/java/com/alibaba/dashscope/common/DashScopeResult.java index 45f3f86e..28e12f19 100644 --- a/src/main/java/com/alibaba/dashscope/common/DashScopeResult.java +++ b/src/main/java/com/alibaba/dashscope/common/DashScopeResult.java @@ -9,6 +9,7 @@ import com.alibaba.dashscope.utils.ApiKeywords; import com.alibaba.dashscope.utils.EncryptionUtils; import com.alibaba.dashscope.utils.JsonUtils; +import com.google.gson.JsonElement; import com.google.gson.JsonObject; import java.nio.ByteBuffer; import java.util.List; @@ -36,7 +37,15 @@ protected T fromResponse(Protocol protocol, NetworkResponse r this.setHeaders(changeHeaders(response.getHeaders())); if (protocol == Protocol.WEBSOCKET) { if (response.getBinary() == null) { - fromWebSocketMessage(response.getMessage()); + String message = response.getMessage(); + if (message == null || message.isEmpty()) { + log.warn( + "WebSocket response message is null or empty, httpStatusCode: {}", + response.getHttpStatusCode()); + setInternalError(); + return (T) this; + } + fromWebSocketMessage(message); } else { this.output = response.getBinary(); } @@ -50,11 +59,15 @@ protected T fromResponse(Protocol protocol, NetworkResponse r return (T) this; } JsonObject jsonObject = parseJson(message, "Failed to parse HTTP response as JSON: {}"); - if (jsonObject == null) return (T) this; + if (jsonObject == null) { + return (T) this; + } if (response.getHttpStatusCode() != null) { this.setStatusCode(response.getHttpStatusCode()); } + handleOutputField(jsonObject); populateFromHttpJson(jsonObject); + handleDataField(jsonObject); } return (T) this; } @@ -102,7 +115,9 @@ public T fromResponse( } JsonObject jsonObject = parseJson(encryptedMessage, "Failed to parse encrypted HTTP response message"); - if (jsonObject == null) return (T) this; + if (jsonObject == null) { + return (T) this; + } String encryptedOutput = jsonObject.get(ApiKeywords.OUTPUT).isJsonNull() ? null @@ -114,9 +129,15 @@ public T fromResponse( req.getEncryptionConfig().getAESEncryptKey(), req.getEncryptionConfig().getIv()); this.output = parseJson(plainOutput, "Failed to parse decrypted output"); - if (this.output == null) return (T) this; + if (this.output == null) { + return (T) this; + } + } else { + this.output = null; } + handleOutputField(jsonObject); populateFromHttpJson(jsonObject); + handleDataField(jsonObject); return (T) this; } return fromResponse(protocol, response, isFlattenResult); @@ -124,7 +145,9 @@ public T fromResponse( private void fromWebSocketMessage(String message) { JsonObject jsonObject = parseJson(message, "Failed to parse WebSocket message"); - if (jsonObject == null) return; + if (jsonObject == null) { + return; + } if (jsonObject.has(ApiKeywords.HEADER)) { JsonObject headers = jsonObject.get(ApiKeywords.HEADER).getAsJsonObject(); if (headers.has(ApiKeywords.TASKID)) { @@ -157,13 +180,23 @@ private void fromWebSocketMessage(String message) { } } - private void populateFromHttpJson(JsonObject jsonObject) { - if (jsonObject.has(ApiKeywords.OUTPUT)) { - this.output = - jsonObject.get(ApiKeywords.OUTPUT).isJsonNull() - ? null - : jsonObject.get(ApiKeywords.OUTPUT).getAsJsonObject(); + private void handleOutputField(JsonObject jsonObject) { + // Handle OUTPUT field first + if (this.output == null && jsonObject.has(ApiKeywords.OUTPUT)) { + JsonElement outputElement = jsonObject.get(ApiKeywords.OUTPUT); + this.output = (outputElement == null || outputElement.isJsonNull()) ? null : outputElement; + } + } + + private void handleDataField(JsonObject jsonObject) { + // Handle DATA field only if OUTPUT was not present + if (this.output == null && jsonObject.has(ApiKeywords.DATA)) { + jsonObject.remove(ApiKeywords.REQUEST_ID); + this.output = jsonObject; } + } + + private void populateFromHttpJson(JsonObject jsonObject) { if (jsonObject.has(ApiKeywords.USAGE)) { this.setUsage( jsonObject.get(ApiKeywords.USAGE).isJsonNull() @@ -187,10 +220,6 @@ private void populateFromHttpJson(JsonObject jsonObject) { jsonObject.has(ApiKeywords.MESSAGE) && !jsonObject.get(ApiKeywords.MESSAGE).isJsonNull() ? jsonObject.get(ApiKeywords.MESSAGE).getAsString() : ""); - if (jsonObject.has(ApiKeywords.DATA)) { - jsonObject.remove(ApiKeywords.REQUEST_ID); - this.output = jsonObject; - } } private JsonObject parseJson(String raw, String logMsg) { From c8d94f411090a1204904c2dc2d3672f1b47715be Mon Sep 17 00:00:00 2001 From: "zhansheng.lzs" Date: Fri, 24 Jul 2026 17:08:25 +0800 Subject: [PATCH 7/7] refactor: simplify DashScopeResult field handling helpers Centralize OUTPUT/DATA handling into handleOutputField/handleDataField and remove redundant inline block in populateFromHttpJson. JsonObject.remove() is safe when key is absent, so drop the unnecessary has() guard. --- .../java/com/alibaba/dashscope/common/DashScopeResult.java | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/main/java/com/alibaba/dashscope/common/DashScopeResult.java b/src/main/java/com/alibaba/dashscope/common/DashScopeResult.java index 28e12f19..2e58dce7 100644 --- a/src/main/java/com/alibaba/dashscope/common/DashScopeResult.java +++ b/src/main/java/com/alibaba/dashscope/common/DashScopeResult.java @@ -129,15 +129,10 @@ public T fromResponse( req.getEncryptionConfig().getAESEncryptKey(), req.getEncryptionConfig().getIv()); this.output = parseJson(plainOutput, "Failed to parse decrypted output"); - if (this.output == null) { - return (T) this; - } } else { this.output = null; } - handleOutputField(jsonObject); populateFromHttpJson(jsonObject); - handleDataField(jsonObject); return (T) this; } return fromResponse(protocol, response, isFlattenResult);