Skip to content

Conversation

@zstack-robot-1
Copy link
Collaborator

Resolves: ZSTAC-66343

Change-Id: I6e6b6f76676c7671786e79696f70746268717864

sync from gitlab !9005

@coderabbitai
Copy link

coderabbitai bot commented Jan 12, 2026

Walkthrough

将项目中散布的字符串型错误/异常构造统一为带组织级 CloudOperationsErrorCode 的错误码形式,Platform.err 系列新增 globalErrorCode 参数并在线路上设置到 ErrorCode,ErrorCode 增加 globalErrorCode 字段及访问器,多个文件引入相应常量并替换错误构造。

Changes

Cohort / File(s) 变更说明
平台核心
core/src/main/java/org/zstack/core/Platform.java, header/src/main/java/org/zstack/header/errorcode/ErrorCode.java
将 Platform.err 等错误构造方法新增 String globalErrorCode 参数并传递/设置到 ErrorCode;在 ErrorCode 中新增 globalErrorCode 字段及 getter/setter。
核心子系统(cloudbus / rest / workflow / thread / ansible / job / gc / singleflight / …)
core/src/main/java/org/zstack/core/... (多文件)
在多处调用改为将 CloudOperationsErrorCode 常量作为首参,并使用带 globalErrorCode 的 err/operr/argerr/inerr 等接口;少数 lambda 参数签名因错误构造链调整同步更新。
计算模块(VM/Host/Allocator/Cluster/Zone)
compute/src/main/java/org/zstack/compute/... (大量文件)
在 VM/Host/Allocator/Cluster/Zone 路径引入 CloudOperationsErrorCode 静态导入,替换原先的字符串式错误为对应 ORG_ZSTACK_COMPUTE_* 系列错误码,VmNicUtils 增加空参数早退。
网络与网络服务
network/src/main/java/org/zstack/network/..., plugin/*/src/main/java/.../network/service/*
在 L2/L3、DHCP、EIP、LB、PortForwarding、SDN 等校验与实现处引入 CloudOperationsErrorCode 并替换为 ORG_ZSTACK_NETWORK_* / ORG_ZSTACK_NETWORK_SERVICE_* 系列错误码。
存储与存储插件(Ceph/NFS/Local 等)
plugin/ceph/..., plugin/nfsPrimaryStorage/..., plugin/localstorage/...
在 Ceph/NFS/Local 及相关插件实现中广泛引入 CloudOperationsErrorCode 并将大量 operr/argerr/err 替换为 ORG_ZSTACK_STORAGE_* 前缀的错误码调用。
镜像 / 长任务 / 控制台 / 配置
image/*, longjob/*, console/*, configuration/*
在多处校验与失败路径替换为对应 ORG_ZSTACK_IMAGE_、ORG_ZSTACK_LONGJOB_、ORG_ZSTACK_CONSOLE_、ORG_ZSTACK_CONFIGURATION_ 错误码调用。
身份与授权
identity/src/main/java/org/zstack/identity/*
在会话、认证、RBAC、配额、Account 等校验路径替换为 ORG_ZSTACK_IDENTITY_* 错误码并加入静态导入;错误构造行为保持一致。
插件与其它子系统
plugin/*, externalservice, mediator, ldap, acl, applianceVm, kvm, iscsi
各插件内部大量错误路径改为使用相应 ORG_ZSTACK__ 错误码,模式一致:静态导入 + 将原字符串作为参数附带到错误码调用中。
零散修复
compute/src/main/java/org/zstack/compute/VmNicUtils.java 等单文件改动
对空输入增加早退;若干局部文件仅替换错误字符串为错误码常量。

Sequence Diagram(s)

(跳过 — 变更主要为错误编码与错误构造链的标准化,未引入新的跨组件顺序交互)

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 分钟

Poem

🐰 我在代码林中轻轻跳,
错误有了编号亮晶晶,
每条报错都有家与号,
查起故障来也不慌慌,
胡萝卜信封贴上标签 🍀🫙

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.82% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed PR标题'[error code]: global error code'遵循了指定的格式[scope]: ,长度为40个字符,在72字符限制内,且清晰描述了此PR的主要目的是添加全局错误码功能。
Description check ✅ Passed PR描述包含了JIRA工单号(ZSTAC-66343)、提交ID和同步信息,与changeset相关。虽然描述简洁,但与changeset的主要目的相关,即在多个模块中引入CloudOperationsErrorCode并用全局错误码替换硬编码的错误消息。

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings

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

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 18

Note

Due to the large number of review comments, Critical, Major severity comments were prioritized as inline comments.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (35)
core/src/main/java/org/zstack/core/salt/SaltSetupMinionJob.java (1)

156-156: 日志消息中存在中文字符。

根据编码规范,代码中不应包含中文字符。此处使用了中文冒号 而非英文冒号 :

修复建议
-                    logger.warn(String.format("FileInputStream close IOException:%s", e.getMessage()));
+                    logger.warn(String.format("FileInputStream close IOException: %s", e.getMessage()));
header/src/main/java/org/zstack/header/errorcode/ErrorCode.java (1)

76-84: 拷贝构造函数缺少 globalErrorCode 字段的复制。

新增的 globalErrorCode 字段未在拷贝构造函数中复制,这会导致通过此构造函数创建的 ErrorCode 副本丢失全局错误码信息。

🐛 建议修复
 public ErrorCode(ErrorCode other) {
     this.code = other.code;
     this.description = other.description;
     this.details = other.details;
     this.elaboration = other.elaboration;
     this.messages = other.messages;
     this.cause = other.cause;
     this.location = other.location;
+    this.globalErrorCode = other.globalErrorCode;
 }
plugin/ceph/src/main/java/org/zstack/storage/ceph/primary/CephRequiredUrlParser.java (1)

27-37: 错误码添加正确,但存在潜在的空指针异常风险。

错误码 ORG_ZSTACK_STORAGE_CEPH_PRIMARY_10051 的添加符合 PR 的全局错误码规范。

但请注意第 36 行的潜在问题:当 protocol 不是 "volume" 或 "ceph" 时,uriParsers.get(protocol) 会返回 null,导致空指针异常。建议增加对未知协议的处理:

🐛 建议的修复方案
     public static InstallPath getInstallPathFromUri(String requiredUrl) {
         String protocol;
         try {
             protocol = new URI(requiredUrl).getScheme();
         } catch (URISyntaxException e) {
             throw new OperationFailureException(
                     argerr(ORG_ZSTACK_STORAGE_CEPH_PRIMARY_10051, "invalid uri, correct example is ceph://$POOLNAME/$VOLUMEUUID or volume://$VOLUMEUUID"));
         }
 
-        return uriParsers.get(protocol).parseUri(requiredUrl);
+        AbstractUriParser parser = uriParsers.get(protocol);
+        if (parser == null) {
+            throw new OperationFailureException(
+                    argerr(ORG_ZSTACK_STORAGE_CEPH_PRIMARY_10051, "unsupported protocol[%s], correct example is ceph://$POOLNAME/$VOLUMEUUID or volume://$VOLUMEUUID", protocol));
+        }
+        return parser.parseUri(requiredUrl);
     }
plugin/ceph/src/main/java/org/zstack/storage/ceph/primary/CephSnapshotProtector.java (1)

27-37: 全局错误码添加正确,但存在预先存在的控制流缺陷。

错误码 ORG_ZSTACK_STORAGE_CEPH_PRIMARY_10034 的添加符合规范。

但请注意第 28-30 行存在一个预先存在的 bug:当 irregularThirdPartySnapshotInstallPath 为 true 时,调用了 completion.success() 却没有 return,导致代码继续执行到第 32-37 行,可能会再次调用 completion.fail()completion.success(),造成回调被多次调用。

🐛 建议修复缺失的 return 语句
         boolean irregularThirdPartySnapshotInstallPath = snapshot.getPrimaryStorageUuid() != null && CephSystemTags.THIRDPARTY_PLATFORM.hasTag(snapshot.getPrimaryStorageUuid());
         if (irregularThirdPartySnapshotInstallPath) {
             completion.success();
+            return;
         }
core/src/main/java/org/zstack/core/encrypt/EncryptFacadeImpl.java (1)

219-232: 异常会被外层 catch 捕获,导致错误码丢失且日志混乱

存在以下问题:

  1. 第 221 行抛出的 OperationFailureException 会被第 230 行的 catch (Exception e) 捕获,导致全局错误码实际上并未传递给上层调用者。
  2. 发生加密错误时会产生双重日志:先是 ERROR 级别(第 220 行),然后是 DEBUG 级别(第 231 行)。
  3. catch 块的日志信息 "decrypt... origin password error" 具有误导性,因为实际错误发生在加密阶段。

如果意图是继续处理其他记录,建议使用 continue 而非抛出异常;如果意图是快速失败,则应将异常抛出到 SQLBatch 块之外。

🔧 建议修复
                            ErrorableValue<String> encrypt = encrypt(decryptedString, key);
                            if (encrypt.error != null) {
-                               logger.error(String.format("Encryption error : %s", encrypt.error));
-                               throw new OperationFailureException(operr(ORG_ZSTACK_CORE_ENCRYPT_10000, "Encryption error : %s", encrypt.error));
+                               logger.error(String.format("encrypt[className: %s, uuid: %s] with new key error: %s", className, uuid, encrypt.error));
+                               continue;
                            }

                            String sql = String.format("update %s set %s = :encrypted where uuid = :uuid", className, field.getName());
                            // ...
                        } catch (Exception e) {
-                           logger.debug(String.format("decrypt[className: %s, uuid: %s] origin password error because : %s", className, uuid, e.getMessage()));
+                           logger.debug(String.format("encryptWithNewKey[className: %s, uuid: %s] error: %s", className, uuid, e.getMessage()));
                        }
plugin/directory/src/main/java/org/zstack/directory/DirectoryApiInterceptor.java (2)

117-124: 需要对 msg.getName() 进行 trim 处理。

根据 Interceptor 编码规范,来自 API Message 的外部字符串参数应进行 trim 处理,以防止用户在浏览器中复制粘贴时带入空格、换行符等不可见字符。

此外,错误消息中包含中文标点符号 ()【】,根据编码规范"代码里不应当有有中文,包括报错、注释等",建议使用英文或 ASCII 字符表示。

建议的修复
 private void validate(APIUpdateDirectoryMsg msg) {
-    boolean result = CharacterUtils.checkCharactersByRegex(regex, msg.getName());
+    String name = msg.getName() != null ? msg.getName().trim() : null;
+    boolean result = CharacterUtils.checkCharactersByRegex(regex, name);
     if (!result) {
         throw new ApiMessageInterceptionException(argerr(ORG_ZSTACK_DIRECTORY_10002, "name contains unsupported characters," +
                 " name can only contain Chinese characters, English letters, " +
-                "numbers, spaces, and the following characters: ()()【】@._-+ "));
+                "numbers, spaces, and the following characters: () [] @._-+ "));
     }
 }

基于编码规范和学习记录。


126-134: 需要对 msg.getName() 进行 trim 处理。

APIUpdateDirectoryMsg 的验证方法存在相同问题:

  1. 缺少对 getName() 的 trim 处理
  2. 错误消息中包含中文标点符号
建议的修复
 private void validate(APICreateDirectoryMsg msg) {
     //judge whether special characters are included
-    boolean result = CharacterUtils.checkCharactersByRegex(regex, msg.getName());
+    String name = msg.getName() != null ? msg.getName().trim() : null;
+    boolean result = CharacterUtils.checkCharactersByRegex(regex, name);
     if (!result) {
         throw new ApiMessageInterceptionException(argerr(ORG_ZSTACK_DIRECTORY_10003, "name contains unsupported characters," +
                 " name can only contain Chinese characters, English letters, " +
-                "numbers, spaces, and the following characters: ()()【】@._-+ "));
+                "numbers, spaces, and the following characters: () [] @._-+ "));
     }
 }

基于编码规范和学习记录。

compute/src/main/java/org/zstack/compute/allocator/BackupStorageSelectPrimaryStorageAllocatorFlow.java (1)

70-77: 发现潜在 Bug:错误消息参数不匹配

错误消息中提到 backup storage[uuid:%s, type:%s],但第 75 行传入的是 spec.getImage().getType()(镜像类型),而非备份存储类型。应使用第 54 行定义的 type 变量。

对比第 82-85 行的正确实现,该处使用了 type 变量。

🐛 建议修复
             result = findHostsByPrimaryStorageTypes(possiblePrimaryStorageTypes);
             if (result.isEmpty()) {
                 String name = spec.getImage().getName();
                 throw new OperationFailureException(operr(
                 ORG_ZSTACK_COMPUTE_ALLOCATOR_10007,         "The image[uuid:%s, name:%s] is on the backup storage[uuid:%s, type:%s] that requires to work with primary storage[types:%s]," +
                                 "however, no host found suitable to work with those primary storage", spec.getImage().getUuid(), name,
-                        spec.getRequiredBackupStorageUuid(),spec.getImage().getType(), possiblePrimaryStorageTypes
+                        spec.getRequiredBackupStorageUuid(), type, possiblePrimaryStorageTypes
                 ));
             }
configuration/src/main/java/org/zstack/configuration/ConfigurationManagerImpl.java (1)

1085-1113: DiskOffering 创建消息校验疑似匹配了错误的 SystemTag 类型(会导致校验失效)
DiskOfferingUserConfigValidator.validateSystemTagInCreateMessage() 里用的是 InstanceOfferingSystemTags.INSTANCE_OFFERING_USER_CONFIG.isMatch(sysTag)(Line 1109),看起来应当匹配 DiskOfferingSystemTags.DISK_OFFERING_USER_CONFIG;否则 DiskOffering 的 userdata tag 计数/重复校验可能不会触发。另外错误提示文本也建议对应 diskOffering(当前仍是 instanceOffering)。

建议修改(最小 diff)
@@
             @Override
             public void validateSystemTagInCreateMessage(APICreateMessage msg) {
                 int userdataTagCount = 0;
                 for (String sysTag : msg.getSystemTags()) {
-                    if (InstanceOfferingSystemTags.INSTANCE_OFFERING_USER_CONFIG.isMatch(sysTag)) {
+                    if (DiskOfferingSystemTags.DISK_OFFERING_USER_CONFIG.isMatch(sysTag)) {
                         if (userdataTagCount > 0) {
                             throw new OperationFailureException(argerr(
-                            ORG_ZSTACK_CONFIGURATION_10004,         "Shouldn't be more than one systemTag for one instanceOffering."));
+                            ORG_ZSTACK_CONFIGURATION_10004,         "Shouldn't be more than one systemTag for one diskOffering."));
                         }
                         userdataTagCount++;
 
                         check(msg.getResourceUuid(), sysTag);
                     }
                 }
             }
core/src/main/java/org/zstack/core/workflow/AsyncWorkFlowChain.java (1)

153-160: 错误码常量使用正确,但日志消息有拼写错误。

错误码 ORG_ZSTACK_CORE_WORKFLOW_10002 已正确添加到 inerr() 调用中,符合全局错误码规范。但第155行的日志消息中,"unhandle exception" 应改为 "unhandled exception"。

plugin/kvm/src/main/java/org/zstack/kvm/KVMHostFactory.java (1)

780-791: 格式化字符串参数未被使用

第 790 行的错误信息 "pci bridge need a value greater than 0 and lower than 32" 没有格式化占位符 %s,但却传入了参数 KVMSystemTags.VM_PREDEFINED_PCI_BRIDGE_NUM_TOKEN。该参数将被忽略,导致错误信息缺少具体的标签名称。

另外,根据编码规范,错误信息应使用正确的英文语法:"need" 应为 "needs"。

🐛 建议修复
             if (num <= 0 || num > 31) {
-                throw new ApiMessageInterceptionException(argerr(ORG_ZSTACK_KVM_10008, "pci bridge need a value greater than 0 and lower than 32", KVMSystemTags.VM_PREDEFINED_PCI_BRIDGE_NUM_TOKEN));
+                throw new ApiMessageInterceptionException(argerr(ORG_ZSTACK_KVM_10008, "%s needs a value greater than 0 and less than 32", KVMSystemTags.VM_PREDEFINED_PCI_BRIDGE_NUM_TOKEN));
             }
plugin/localstorage/src/main/java/org/zstack/storage/primary/local/LocalStorageUtils.java (1)

133-140: 错误信息里的 needed 建议使用 s.getSize(),避免扩展点修改后信息不准确
Line 138-139 目前 needed 仍使用入参 size,但前面扩展点可能修改 s.setSize(...);此处仅影响报错信息准确性,但排障时会误导。

建议改动
-                throw new OperationFailureException(operr(ORG_ZSTACK_STORAGE_PRIMARY_LOCAL_10057, "host[uuid: %s] of local primary storage[uuid: %s] doesn't have enough capacity" +
-                        "[current: %s bytes, needed: %s]", hostUuid, self.getUuid(), ref.getAvailableCapacity(), size));
+                throw new OperationFailureException(operr(ORG_ZSTACK_STORAGE_PRIMARY_LOCAL_10057, "host[uuid: %s] of local primary storage[uuid: %s] doesn't have enough capacity" +
+                        "[current: %s bytes, needed: %s]", hostUuid, self.getUuid(), ref.getAvailableCapacity(), s.getSize()));
plugin/localstorage/src/main/java/org/zstack/storage/primary/local/LocalStorageDefaultAllocateCapacityFlow.java (1)

188-193: 在第 185 行添加 null 检查,避免潜在的 NullPointerException

dbf.findByUuid() 方法可能返回 null,但第 187 行直接调用 getType() 而未进行 null 检查。需要在第 185 行后添加 null 验证:

PrimaryStorageVO requiredPrimaryStorageUuidForDataVolume = dbf.findByUuid(spec.getRequiredPrimaryStorageUuidForDataVolume(), PrimaryStorageVO.class);
if (requiredPrimaryStorageUuidForDataVolume == null) {
    // handle null case
}

错误码 ORG_ZSTACK_STORAGE_PRIMARY_LOCAL_10085 的使用正确,错误消息英文表达清晰。

plugin/sdnController/src/main/java/org/zstack/sdnController/hardwareVxlan/HardwareVxlanNetworkPoolFactory.java (1)

156-163: 英文语法错误:"not equals" 应改为 "does not equal"。

Platform.err() 的使用方式正确。但错误消息中 "not equals" 语法不规范。

📝 建议修复
             throw new ApiMessageInterceptionException(Platform.err(ORG_ZSTACK_SDNCONTROLLER_HARDWAREVXLAN_10012, SysErrors.INVALID_ARGUMENT_ERROR,
-                    String.format("the zone uuid provided not equals to zone uuid of pool [%s], please correct it or do not fill it",
+                    String.format("the zone uuid provided does not equal the zone uuid of pool [%s], please correct it or leave it empty",
                             msg.getPoolUuid())
             ));
plugin/eip/src/main/java/org/zstack/network/service/eip/EipApiInterceptor.java (3)

119-131: 错误信息与逻辑不符

!found 时,表示 IP 不属于该 vmNic,但错误信息 "ip [uuid:%s] is attached to vm nic [%s]" 却表示 IP 已附加。应修正为 "is not attached""does not belong to"

🐛 建议修复
     if (!found) {
-        throw new ApiMessageInterceptionException(argerr(ORG_ZSTACK_NETWORK_SERVICE_EIP_10007, "ip [uuid:%s] is attached to vm nic [%s]", guestIpUuid, vmNicUuid));
+        throw new ApiMessageInterceptionException(argerr(ORG_ZSTACK_NETWORK_SERVICE_EIP_10007, "ip [uuid:%s] is not attached to vm nic [uuid:%s]", guestIpUuid, vmNicUuid));
     }

312-325: 英文语法错误

错误信息 "maybe you should wait the vm process complete" 语法不正确,应改为 "maybe you should wait for the vm process to complete"。根据编码规范,报错应使用正确的英文。

♻️ 建议修复
         if (state != null && !EipConstant.attachableVmStates.contains(state)){
             throw new ApiMessageInterceptionException(operr(
-            ORG_ZSTACK_NETWORK_SERVICE_EIP_10022,         "vm state[%s] is not allowed to operate eip, maybe you should wait the vm process complete",
+            ORG_ZSTACK_NETWORK_SERVICE_EIP_10022, "vm state[%s] is not allowed to operate eip, maybe you should wait for the vm process to complete",
                     state.toString()));
         }

327-339: 英文语法错误

错误信息存在多处语法问题:

  • "is not allowed add""is not allowed to add"
  • "vmNic exist portForwarding""vmNic has portForwarding""there exists portForwarding"

根据编码规范,报错应使用正确的英文。

♻️ 建议修复
         if (!cidrs.isEmpty()) {
             throw new ApiMessageInterceptionException(operr(
-            ORG_ZSTACK_NETWORK_SERVICE_EIP_10023,         "vmNic uuid[%s] is not allowed add eip, because vmNic exist portForwarding with allowedCidr rule",
+            ORG_ZSTACK_NETWORK_SERVICE_EIP_10023, "vmNic[uuid:%s] is not allowed to add eip, because vmNic has portForwarding with allowedCidr rule",
                     vmNicUuid));
         }
plugin/acl/src/main/java/org/zstack/acl/AccessControlListApiInterceptor.java (1)

166-195: 错误消息存在语法错误。

根据 coding guidelines,代码中的英文应当正确无拼写错误。多处错误消息使用了 "is not validate domain/url",其中 validate 是动词,应改为形容词 valid

受影响的行:168, 179, 184, 191

📝 建议的语法修正
-    throw new ApiMessageInterceptionException(argerr(ORG_ZSTACK_ACL_10011, "domain[%s] is not validate domain", msg.getDomain()));
+    throw new ApiMessageInterceptionException(argerr(ORG_ZSTACK_ACL_10011, "domain[%s] is not a valid domain", msg.getDomain()));
-    throw new ApiMessageInterceptionException(argerr(ORG_ZSTACK_ACL_10012, "url[%s] is not validate url", msg.getUrl()));
+    throw new ApiMessageInterceptionException(argerr(ORG_ZSTACK_ACL_10012, "url[%s] is not a valid url", msg.getUrl()));
-    throw new ApiMessageInterceptionException(argerr(ORG_ZSTACK_ACL_10013, "url[%s] is not validate url", msg.getUrl()));
+    throw new ApiMessageInterceptionException(argerr(ORG_ZSTACK_ACL_10013, "url[%s] is not a valid url", msg.getUrl()));
compute/src/main/java/org/zstack/compute/vm/VmInstanceApiInterceptor.java (1)

175-385: 需要对 Message 参数进行 trim() 处理,并避免使用 TypedQuery

  1. Message 参数缺少 trim() 处理(Interceptor 必需)

    • msg.getVmInstanceUuid()msg.getL3NetworkUuids()msg.getBackupStorageUuid()msg.getVmNicUuid()msg.getDestL3NetworkUuid()msg.getStaticIp() 等参数在使用前应调用 .trim() 处理空格和换行符。用户在浏览器复制粘贴的数据可能包含额外的空白字符。
  2. 避免使用 TypedQuery

    • validate(APIChangeVmNicNetworkMsg msg) 方法中使用了 dbf.getEntityManager().createQuery(sql, Tuple.class),应改用 SQL.New()Q.New() API。
core/src/main/java/org/zstack/core/upgrade/UpgradeChecker.java (2)

205-214: operr(...) 的英文报错文案建议修正拼写/表达,并避免把白名单全集直接输出到错误里。

  • Line 211:ApiAPI;配置名建议与实际 GlobalConfig key 保持一致;%s 打印整个 grayScaleApiWhiteList 可能非常长(影响日志/返回体可读性)。
  • Line 296:sb.toString() 里带大量换行/字段信息没问题,但建议确认该信息不会被直接透出到不该看到的调用方(例如外部 API 响应)。
Diff(示例)
-                return operr(ORG_ZSTACK_CORE_UPGRADE_10000, "Api: %s is not allowed by allowedApiListGrayscaleUpgrading: %s.",
+                return operr(ORG_ZSTACK_CORE_UPGRADE_10000, "API: %s is not allowed by allowedApiListGrayscaleUpgrading (size: %d).",
                         className,
-                        grayScaleApiWhiteList);
+                        grayScaleApiWhiteList.size());

Also applies to: 293-297


388-417: argerr(...) 的英文报错文案存在多处语法/大小写问题,建议一并打磨(符合“英文无拼写错误”的规范)。
涉及 Line 395 / 406 / 415:

  • Disable grayscale upgrade by %s \n before ...:不建议在句中插入 \n,且句子结构别扭。
  • Can not found ...Cannot find ... / Failed to find ...
  • osOSbefore update osbefore updating the OSagent version is not upgradedagent version has not been upgraded
Diff(示例)
-                    argerr(ORG_ZSTACK_CORE_UPGRADE_10002, "Disable grayscale upgrade by %s \n before you want to update whole cluster's hosts' os." +
-                            " Or try update cluster os with specific hostUuid instead.", UpgradeGlobalConfig.GRAYSCALE_UPGRADE.toString())
+                    argerr(ORG_ZSTACK_CORE_UPGRADE_10002,
+                            "Please disable grayscale upgrade (%s) before updating the OS for all hosts in the cluster. " +
+                                    "Alternatively, specify hostUuid to update a single host.",
+                            UpgradeGlobalConfig.GRAYSCALE_UPGRADE.toString())
             );
 ...
-                    argerr(ORG_ZSTACK_CORE_UPGRADE_10003, "Can not found agent version, upgrade cluster os is not supported during grayscale upgrade")
+                    argerr(ORG_ZSTACK_CORE_UPGRADE_10003,
+                            "Cannot find the agent version. Upgrading the cluster OS is not supported during grayscale upgrade.")
             );
 ...
-                argerr(ORG_ZSTACK_CORE_UPGRADE_10004, "Host[uuid: %s] agent version is not upgraded, please reconnect host before update os", msg.getHostUuid())
+                argerr(ORG_ZSTACK_CORE_UPGRADE_10004,
+                        "Host[uuid: %s] agent version has not been upgraded. Please reconnect the host before updating the OS.",
+                        msg.getHostUuid())
         );
plugin/directory/src/main/java/org/zstack/directory/DirectoryManagerImpl.java (1)

136-143: parentVO 未判空会触发 NPE(Line 137-139),建议补齐并返回明确错误码。 msg.getParentUuid() 属于外部输入,DB 查不到时应走 fail 分支而不是直接 NPE。

修复建议
         } else {
             String groupName = String.format("/%s", msg.getName());
             DirectoryVO parentVO = dbf.findByUuid(msg.getParentUuid(), DirectoryVO.class);
+            if (parentVO == null) {
+                completion.fail(operr(ORG_ZSTACK_DIRECTORY_10007,
+                        "unable to find parent directory[uuid=%s]", msg.getParentUuid()));
+                return;
+            }
             vo.setRootDirectoryUuid(parentVO.getRootDirectoryUuid());
             vo.setGroupName(parentVO.getGroupName() + groupName);
             vo.setParentUuid(msg.getParentUuid());
             //judge whether the same level directory has the same name
             directoryVOS = Q.New(DirectoryVO.class).eq(DirectoryVO_.parentUuid, parentVO.getUuid()).list();
         }
plugin/loadBalancer/src/main/java/org/zstack/network/service/lb/LoadBalancerBase.java (1)

855-859: 错误信息英文拼写/语义不准确(delete vs detach)

Line 855 的信息是 "cloud not delete vip",建议改为 "could not detach/release vip"(并修正 could 拼写)。

🛠️ 建议修复(仅文本)
-                        reply.setError(operr(ORG_ZSTACK_NETWORK_SERVICE_LB_10020, "cloud not delete vip[%s]", vipVO.getUuid()));
+                        reply.setError(operr(ORG_ZSTACK_NETWORK_SERVICE_LB_10020, "could not detach vip[%s] from load balancer", vipVO.getUuid()));
plugin/ceph/src/main/java/org/zstack/storage/ceph/backup/CephBackupStorageMonBase.java (1)

100-108: 英文文案小修:补全句号/空格并保持一致性

Line 105-106 的 "deleted." + "This error can be ignored" 建议加一个空格/句号,避免拼接后读起来别扭(并符合“英文无拼写/语法问题”的规范)。

建议修改
-                    operr(ORG_ZSTACK_STORAGE_CEPH_BACKUP_10012, "cannot update status of the ceph backup storage mon[uuid:%s], it has been deleted." +
-                            "This error can be ignored", uuid)
+                    operr(ORG_ZSTACK_STORAGE_CEPH_BACKUP_10012, "cannot update status of the ceph backup storage mon[uuid:%s], it has been deleted. " +
+                            "This error can be ignored.", uuid)
plugin/ceph/src/main/java/org/zstack/storage/ceph/primary/CephPrimaryStorageMonBase.java (1)

293-302: SshException 直接用 ex.toString() 返回给上层:建议确认不会包含敏感信息/命令细节
如果 SshException 详情里可能包含执行命令、目标路径等,可能会在 API 错误详情中外泄;更稳妥是用更克制的 message 并在日志里记录异常栈。

plugin/ceph/src/main/java/org/zstack/storage/ceph/backup/CephBackupStorageBase.java (2)

746-772: HttpCaller.doCall() 最终失败未携带已收集的 errorCodes,会丢失根因信息
前面 fail() 分支已把多次失败的 ErrorCode 放进 errorCodes,但 !it.hasNext() 时只返回一条新错误,排障信息变少。建议把 causes 带上(若 operr(globalCode, ErrorCodeList, ...) 重载存在)。

Proposed fix
         private void doCall() {
             if (!it.hasNext()) {
-                callback.fail(operr(ORG_ZSTACK_STORAGE_CEPH_BACKUP_10001, "all monitors cannot execute http call[%s]", path));
+                ErrorCodeList causes = new ErrorCodeList().causedBy(errorCodes);
+                callback.fail(operr(ORG_ZSTACK_STORAGE_CEPH_BACKUP_10001, causes,
+                        "all monitors cannot execute http call[%s]", path));
 
                 return;
             }

1758-1763: 镜像不在备份存储上的错误码补齐:OK(可顺手修正字符串空格风格)
当前为 image[uuid: %s](uuid 冒号后有空格),如果项目里统一风格是 uuid:%s,建议顺手统一。

compute/src/main/java/org/zstack/compute/allocator/FilterFlow.java (1)

36-41: fail() 调用后缺少 return 语句,导致逻辑错误。

candidates.isEmpty() 为 true 时,调用 fail() 后代码继续执行,最终会调用 next(candidates) 并传入空列表。这与预期的失败行为不一致。

对比 HostCapacityAllocatorFlow.java 第 65-70 行的实现,使用了 if-else 结构正确处理了这种情况。

🐛 建议修复:在 fail() 后添加 return
             if (candidates.isEmpty()) {
                 fail(Platform.operr(ORG_ZSTACK_COMPUTE_ALLOCATOR_10037, "after filtering, HostAllocatorFilterExtensionPoint[%s] returns zero candidate host, it means: %s", filter.getClass().getSimpleName(), filter.filterErrorReason()));
+                return;
             }
         }

         next(candidates);
plugin/sdnController/src/main/java/org/zstack/sdnController/SdnControllerApiInterceptor.java (1)

279-300: 异常捕获范围过广,会导致错误码被覆盖

catch (Exception e) 会捕获所有异常,包括在 try 块内部 279-291 行抛出的 ApiMessageInterceptionException。当 start > end 或检测到 VLAN 范围重叠时,本应返回错误码 1002810029,但会被 catch 块捕获后重新抛出错误码 10030,导致错误信息不准确。

🐛 建议修复:仅捕获 NumberFormatException
             try {
                 int start = Integer.parseInt(vlans.get(0));
                 int end = Integer.parseInt(vlans.get(1));
                 if (start > end) {
                     throw new ApiMessageInterceptionException(argerr(ORG_ZSTACK_SDNCONTROLLER_10028, "could not change sdn controller, " +
                             "because vlan range[%s] is not in the correct format", range));
                 }
                 
                 for (SdnVlanRange vrange : sdnVlanRanges) {
                     if (isOverlappedVlanRange(start, end, vrange.startVlan, vrange.endVlan)) {
                         throw new ApiMessageInterceptionException(argerr(ORG_ZSTACK_SDNCONTROLLER_10029, "could not change sdn controller, " +
                                 "because vlan range[%s] is overlapped with other vlan range", range));
                     }
                 }
                 SdnVlanRange vlanRange = new SdnVlanRange();
                 vlanRange.startVlan = start;
                 vlanRange.endVlan = end;
                 sdnVlanRanges.add(vlanRange);
-            } catch (Exception e) {
+            } catch (NumberFormatException e) {
                 throw new ApiMessageInterceptionException(argerr(ORG_ZSTACK_SDNCONTROLLER_10030, "could not change sdn controller, " +
                         "because vlan range[%s] is not in the correct format", range));
             }
console/src/main/java/org/zstack/console/ManagementServerConsoleProxyBackend.java (1)

566-572: 格式化字符串缺少参数

错误消息包含 %d 格式占位符,但未传递对应的参数 rstportRst,会导致错误消息显示不正确或抛出格式化异常。

🐛 建议修复:传递格式化参数
                     if (rst == 0) {
                         trigger.next();
                     } else {
-                        trigger.fail(operr(ORG_ZSTACK_CONSOLE_10003, "failed to configure consoleProxyOverriddenIp[code:%d] or consoleProxyPort[code:%d]"));
+                        trigger.fail(operr(ORG_ZSTACK_CONSOLE_10003, "failed to configure consoleProxyOverriddenIp[code:%d] or consoleProxyPort[code:%d]", rst, portRst));
                     }
plugin/kvm/src/main/java/org/zstack/kvm/KVMHost.java (5)

6392-6419: 严重:Platform.operr(...) 参数顺序导致错误信息拼装错误/丢失(Line 6405)
Platform.operr(ORG_ZSTACK_KVM_10125, response.getError(), "fail to check file ...", ...) 会把 response.getError() 当作 format string,后面的 "fail to check file ..." 反而变成“多余参数”,最终 details 很可能只剩 agent error,丢掉你想表达的业务上下文。

建议修复(示例 diff)
-                        reply.setError(Platform.operr(ORG_ZSTACK_KVM_10125, response.getError(),
-                                "fail to check file %s on host[uuid:%s]", msg.getPaths(), msg.getHostUuid()));
+                        reply.setError(Platform.operr(ORG_ZSTACK_KVM_10125,
+                                "failed to check file %s on host[uuid:%s], because %s",
+                                msg.getPaths(), msg.getHostUuid(), response.getError()));

6295-6348: 严重:超时错误提示的单位不一致(毫秒当成秒)(Line 6305)
cttimeout 是毫秒,但提示文案写的是 seconds,容易误导排障。

建议修复(示例 diff)
-                                reply.setError(operr(ORG_ZSTACK_KVM_10123, "host[%s] not shutdown in %d seconds", msg.getHostUuid(), ctimeout));
+                                reply.setError(operr(ORG_ZSTACK_KVM_10123, "host[%s] not shut down within %d seconds",
+                                        msg.getHostUuid(), timeoutInSec));

2757-2765: 文案与实际允许的 VM 状态不一致,建议对齐(避免误导)
例如逻辑允许 Paused(以及 merge snapshot 场景里还允许 Destroyed),但文案写 “only allowed when vm is Running or Stopped”。

建议修复(仅改文案,避免改变既有行为)
-                throw new OperationFailureException(operr(ORG_ZSTACK_KVM_10054, "cannot do volume snapshot merge when vm[uuid:%s] is in state of %s." +
-                                " The operation is only allowed when vm is Running or Stopped", volume.getUuid(), state));
+                throw new OperationFailureException(operr(ORG_ZSTACK_KVM_10054, "cannot do volume snapshot merge when vm[uuid:%s] is in state of %s." +
+                                " The operation is only allowed when vm is Running/Stopped/Paused/Destroyed", volume.getUuid(), state));
-                throw new OperationFailureException(operr(ORG_ZSTACK_KVM_10057, "vm[uuid:%s] is not Running or Stopped, current state[%s]", msg.getVmUuid(), vmState));
+                throw new OperationFailureException(operr(ORG_ZSTACK_KVM_10057, "vm[uuid:%s] is not Running/Stopped/Paused, current state[%s]", msg.getVmUuid(), vmState));
-                throw new OperationFailureException(operr(ORG_ZSTACK_KVM_10059, "vm[uuid:%s] is not Running or Stopped, current state[%s]", msg.getVmUuid(), vmState));
+                throw new OperationFailureException(operr(ORG_ZSTACK_KVM_10059, "vm[uuid:%s] is not Running/Stopped/Paused, current state[%s]", msg.getVmUuid(), vmState));

Also applies to: 2860-2866, 2976-2983


4144-4163: 英文文案拼写/语法问题(属于本次改行,建议顺手修)

  • faildfailed(Line 4151)
  • doesn't notdoes not(Line 2988)
建议修复(示例 diff)
-                    reply.setError(operr(ORG_ZSTACK_KVM_10079, "Host[%s] update spice channel config faild, because %s", msg.getHostUuid(), ret.getError()));
+                    reply.setError(operr(ORG_ZSTACK_KVM_10079, "Host[%s] update spice channel config failed, because %s", msg.getHostUuid(), ret.getError()));
-                            "kvm host[uuid:%s, name:%s, ip:%s] doesn't not support live snapshot. please stop vm[uuid:%s] and try again",
+                            "kvm host[uuid:%s, name:%s, ip:%s] does not support live snapshot. please stop vm[uuid:%s] and try again",

Also applies to: 2987-2991


1256-1263: inerr 改为 operr,因为 WebSSH 不可达是外部依赖问题而非内部错误

WebSSH 服务器不可达属于外部服务连通性问题,而非内部平台错误。根据代码库的使用规范,外部依赖不可用或连接失败应使用 operr,而 inerr 应仅用于内部系统错误。

🤖 Fix all issues with AI agents
In @compute/src/main/java/org/zstack/compute/allocator/HostAllocatorChain.java:
- Around line 147-150: The catch(Throwable t) in runFlow incorrectly always
calls completion.fail(...), which can be null in dry-run mode and causes an NPE
that hides the original error; change the catch block to mirror done() by
checking isDryRun and invoking dryRunCompletion.fail(...) when dry-run is
active, otherwise invoking completion.fail(...), and ensure you null-check the
chosen completion before calling fail and log the original throwable.

In @compute/src/main/java/org/zstack/compute/vm/VmInstanceBase.java:
- Around line 2917-2921: The error handling uses the newly created
AttachNicToVmReply instance r (whose getError() is null) causing an NPE and
loses the real cause; replace r.getError() usages with reply.getError() when
calling err(...) so the actual error and details from reply are propagated
(i.e., build the error with reply.getError() / reply.getError().getDetails()),
and then bus.reply(msg, r) as before; optionally add a null-check on
reply.getError() and set a generic error if it's unexpectedly null.

In @compute/src/main/java/org/zstack/compute/vm/VmInstanceManagerImpl.java:
- Around line 1148-1150: The methods extEmitterHandleSystemTag and
extEmitterHandleSshKeyPair initialize result with Collections.emptyList(), which
is immutable and causes UnsupportedOperationException when result.add(...) is
later called (e.g., in the msg == null branch). Replace the immutable empty list
with a mutable list (e.g., new ArrayList<>()) for the result variable in both
extEmitterHandleSystemTag and extEmitterHandleSshKeyPair, preserving the
original generic type so subsequent result.add(operr(...)) calls succeed without
throwing.

In
@core/src/main/java/org/zstack/core/externalservice/ExternalServiceManagerImpl.java:
- Around line 99-107: In ExternalServiceManagerImpl, after checking
service.getExternalServiceCapabilities().isReloadConfig() and calling
event.setError(...) when reload is not supported, add an early return so
execution does not continue to the subsequent service.isAlive() check and
service.reload() call; ensure the branch that sets
ORG_ZSTACK_CORE_EXTERNALSERVICE_10002 exits immediately (preventing
service.reload() from being invoked and preventing the error from being
overwritten by ORG_ZSTACK_CORE_EXTERNALSERVICE_10003).

In @identity/src/main/java/org/zstack/identity/QuotaUtil.java:
- Around line 35-38: The static import line "import static
org.zstack.utils.clouderrorcode.CloudOperationsErrorCode.*" is accidentally
concatenated with a Javadoc opener ("/*") causing a syntax error; separate them
by ending the import with a semicolon and placing the Javadoc comment start on
the following line (e.g., keep the import of CloudOperationsErrorCode.* as its
own statement and move the "/**" that begins the class/file comment onto the
next line before the existing comment block or class declaration such as in
QuotaUtil).

In @network/src/main/java/org/zstack/network/l3/L3NetworkApiInterceptor.java:
- Around line 38-39: In L3NetworkApiInterceptor, fix the incorrect error
messages and format parameters: change the delete message `"could delete ip
address"` to `"could not delete ip address"` where delete IP failures are
reported; in the end-IP validation use msg.getEndIp() (instead of
msg.getStartIp()) when inserting the end IP into the error message; and for the
prefix-related validation messages that currently pass msg.getL3NetworkUuid(),
replace those arguments with msg.getPrefix() so the `"prefix [%s]"` placeholders
show the actual prefix (update both occurrences corresponding to the current
checks at the two prefix validation sites).

In
@plugin/acl/src/main/java/org/zstack/acl/AccessControlListApiInterceptor.java:
- Around line 189-192: The error message in AccessControlListApiInterceptor uses
the wrong argument when URL validation fails:
AccessControlListUtils.isValidateUrl(msg.getUrl()) triggers an
ApiMessageInterceptionException with argerr(..., "url[%s] is not validate url",
msg.getDomain()) but should pass the URL; change the formatted argument to
msg.getUrl() so the exception message reflects the failing value (keep the
thrown ApiMessageInterceptionException, argerr call and surrounding logic
intact).

In @plugin/ceph/src/main/java/org/zstack/storage/ceph/MonUri.java:
- Around line 67-68: The error messages in MonUri throw
OperationFailureException with the raw url (which can contain
sshUsername:sshPassword@host) exposing sensitive credentials; implement a
maskPassword(String url) helper in MonUri that redacts the password portion
(e.g., replace characters between ':' and '@' with **** or remove password) and
update all operr(...) calls in MonUri (the throw sites currently using url in
messages) to pass maskPassword(url) instead of url; apply the same masking to
the other operr usages in this class so no raw password appears in logs or
exceptions.

In
@plugin/flatNetworkProvider/src/main/java/org/zstack/network/service/flat/FlatDhcpBackend.java:
- Around line 2182-2183: The call to operr in FlatDhcpBackend within the
ReturnValueCompletion<KvmResponseWrapper> block uses the wrong parameter
order/type (operr(ORG_ZSTACK_NETWORK_SERVICE_FLAT_10028, rsp.getError(),
"operation error, because:%s", rsp.getError())), causing a compilation error
because rsp.getError() is a String while that overload expects an ErrorCode;
change the call to use the String-format overload by passing the global error
code first, then the format string, then rsp.getError() (i.e., use
operr(ORG_ZSTACK_NETWORK_SERVICE_FLAT_10028, "operation error, because:%s",
rsp.getError())) to match other usages and fix the type mismatch.

In @plugin/ldap/src/main/java/org/zstack/ldap/LdapApiInterceptor.java:
- Line 129: The format string in the argerr call inside LdapApiInterceptor is
wrong ("[%,%s]") and will cause runtime formatting errors; update the call to
use a correct format string "[%s,%s]" and ensure the arguments remain (type,
LdapConstant.OpenLdap.TYPE, LdapConstant.WindowsAD.TYPE) so the error message
produced by argerr(ORG_ZSTACK_LDAP_10010, ...) correctly prints the provided
type and the two valid constants.

In
@plugin/nfsPrimaryStorage/src/main/java/org/zstack/storage/primary/nfs/NfsPrimaryStorageFactory.java:
- Around line 116-119: The error message in NfsPrimaryStorageFactory throws an
OperationFailureException using operr(ORG_ZSTACK_STORAGE_PRIMARY_NFS_10000,
"...%s) with parameters passed as psUuid, vmUuid, volumeUuid which mismatches
the template order (psUuid, volumeUuid, vmUuid); fix by swapping the last two
arguments so the operr call passes psUuid, volumeUuid, vmUuid (update the throw
in NfsPrimaryStorageFactory where OperationFailureException is created).

In
@plugin/nfsPrimaryStorage/src/main/java/org/zstack/storage/primary/nfs/NfsPrimaryStorageKVMBackend.java:
- Around line 1256-1261: The log/error message templates in
NfsPrimaryStorageKVMBackend (logger.warn and completion.fail calls) incorrectly
include the literal text "installPath, pinv.getUuid(), rsp.getError()" inside
the format string; fix by using proper formatting: remove the stray literal and
pass installPath, pinv.getUuid(), rsp.getError() as arguments to String.format
(for logger.warn) and to operr(...) (for completion.fail) so both messages
display the actual variable values.

Comment on lines 147 to 150
} catch (Throwable t) {
logger.warn("unhandled throwable", t);
completion.fail(inerr(t.toString()));
completion.fail(inerr(ORG_ZSTACK_COMPUTE_ALLOCATOR_10019, t.toString()));
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

cat -n compute/src/main/java/org/zstack/compute/allocator/HostAllocatorChain.java | head -200

Repository: MatheMatrix/zstack

Length of output: 180


🏁 Script executed:

# Check if completion field could be null during dry-run
rg -n "dryRunCompletion|completion\s*=" compute/src/main/java/org/zstack/compute/allocator/HostAllocatorChain.java -A 2

Repository: MatheMatrix/zstack

Length of output: 180


🏁 Script executed:

# Search for inerr method signature
rg -n "ErrorCode\s+inerr\s*\(" --type=java -A 3 | head -50

Repository: MatheMatrix/zstack

Length of output: 439


🏁 Script executed:

rg "private.*completion|this\.completion\s*=" --type=java -B 2 -A 2 | grep -A 5 -B 5 "HostAllocatorChain"

Repository: MatheMatrix/zstack

Length of output: 2722


🏁 Script executed:

rg "dryRunCompletion|isDryRun" --type=java | head -40

Repository: MatheMatrix/zstack

Length of output: 4654


🏁 Script executed:

# Search for the specific error code constants
rg "ORG_ZSTACK_COMPUTE_ALLOCATOR_1001[0-9]" --type=java -B 1 -A 1 | head -50

Repository: MatheMatrix/zstack

Length of output: 6395


🏁 Script executed:

# Get the runFlow method to see the exception handling
rg -n "private void runFlow\|} catch \(Throwable" compute/src/main/java/org/zstack/compute/allocator/HostAllocatorChain.java -A 15 | head -80

Repository: MatheMatrix/zstack

Length of output: 44


🏁 Script executed:

# Get a specific range around line 147-150
sed -n '140,160p' compute/src/main/java/org/zstack/compute/allocator/HostAllocatorChain.java

Repository: MatheMatrix/zstack

Length of output: 1095


🏁 Script executed:

# Look for the full context around the allocate and dryRun methods to confirm flow
sed -n '53,85p' compute/src/main/java/org/zstack/compute/allocator/HostAllocatorChain.java

Repository: MatheMatrix/zstack

Length of output: 886


🏁 Script executed:

# Check other exception handlers in this file to see the pattern
rg -n "if \(isDryRun\)" compute/src/main/java/org/zstack/compute/allocator/HostAllocatorChain.java -B 2 -A 2

Repository: MatheMatrix/zstack

Length of output: 819


🏁 Script executed:

# Verify that dryRun() only sets dryRunCompletion and not completion
sed -n '77,87p' compute/src/main/java/org/zstack/compute/allocator/HostAllocatorChain.java

Repository: MatheMatrix/zstack

Length of output: 280


🏁 Script executed:

# Check the exact catch block implementation once more with more context
sed -n '145,155p' compute/src/main/java/org/zstack/compute/allocator/HostAllocatorChain.java

Repository: MatheMatrix/zstack

Length of output: 536


严重:dry-run 场景的异常捕获会导致 NPE,覆盖原始异常

runFlow()catch (Throwable) 分支(第 147-150 行)直接调用 completion.fail(...),但 dryRun() 方法仅设置 dryRunCompletion,将 completion 保留为 null。当异常被捕获时,会在 null 的 completion 上调用 fail(),触发 NullPointerException,进而掩盖真实异常。

代码的其他部分(如 done() 方法)都正确地通过 if (isDryRun) 判断路由到对应的 completion 对象,但该 catch 块未遵循此模式。

建议修复
        } catch (Throwable t) {
            logger.warn("unhandled throwable", t);
-            completion.fail(inerr(ORG_ZSTACK_COMPUTE_ALLOCATOR_10019, t.toString()));
+            ErrorCode ec = inerr(ORG_ZSTACK_COMPUTE_ALLOCATOR_10019, t.toString());
+            if (isDryRun) {
+                dryRunCompletion.fail(ec);
+            } else {
+                completion.fail(ec);
+            }
        }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
} catch (Throwable t) {
logger.warn("unhandled throwable", t);
completion.fail(inerr(t.toString()));
completion.fail(inerr(ORG_ZSTACK_COMPUTE_ALLOCATOR_10019, t.toString()));
}
} catch (Throwable t) {
logger.warn("unhandled throwable", t);
ErrorCode ec = inerr(ORG_ZSTACK_COMPUTE_ALLOCATOR_10019, t.toString());
if (isDryRun) {
dryRunCompletion.fail(ec);
} else {
completion.fail(ec);
}
}
🤖 Prompt for AI Agents
In @compute/src/main/java/org/zstack/compute/allocator/HostAllocatorChain.java
around lines 147 - 150, The catch(Throwable t) in runFlow incorrectly always
calls completion.fail(...), which can be null in dry-run mode and causes an NPE
that hides the original error; change the catch block to mirror done() by
checking isDryRun and invoking dryRunCompletion.fail(...) when dry-run is
active, otherwise invoking completion.fail(...), and ensure you null-check the
chosen completion before calling fail and log the original throwable.

Comment on lines 2917 to 2921
AttachNicToVmReply r = new AttachNicToVmReply();
if (!reply.isSuccess()) {
r.setError(err(VmErrors.ATTACH_NETWORK_ERROR, r.getError(), r.getError().getDetails()));
r.setError(err(ORG_ZSTACK_COMPUTE_VM_10274, VmErrors.ATTACH_NETWORK_ERROR, r.getError(), r.getError().getDetails()));
}
bus.reply(msg, r);
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

错误处理使用了错误的 ErrorCode 来源,且会触发 NPE(必须修)。
当前分支里 r 是新建的 AttachNicToVmReplyr.getError()null,会导致 r.getError().getDetails() 空指针;同时也丢失了真实失败原因(应使用 reply.getError())。

建议修复
                 AttachNicToVmReply r = new AttachNicToVmReply();
                 if (!reply.isSuccess()) {
-                    r.setError(err(ORG_ZSTACK_COMPUTE_VM_10274, VmErrors.ATTACH_NETWORK_ERROR, r.getError(), r.getError().getDetails()));
+                    r.setError(err(ORG_ZSTACK_COMPUTE_VM_10274, VmErrors.ATTACH_NETWORK_ERROR,
+                            reply.getError(), reply.getError().getDetails()));
                 }
                 bus.reply(msg, r);
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
AttachNicToVmReply r = new AttachNicToVmReply();
if (!reply.isSuccess()) {
r.setError(err(VmErrors.ATTACH_NETWORK_ERROR, r.getError(), r.getError().getDetails()));
r.setError(err(ORG_ZSTACK_COMPUTE_VM_10274, VmErrors.ATTACH_NETWORK_ERROR, r.getError(), r.getError().getDetails()));
}
bus.reply(msg, r);
AttachNicToVmReply r = new AttachNicToVmReply();
if (!reply.isSuccess()) {
r.setError(err(ORG_ZSTACK_COMPUTE_VM_10274, VmErrors.ATTACH_NETWORK_ERROR,
reply.getError(), reply.getError().getDetails()));
}
bus.reply(msg, r);
🤖 Prompt for AI Agents
In @compute/src/main/java/org/zstack/compute/vm/VmInstanceBase.java around lines
2917 - 2921, The error handling uses the newly created AttachNicToVmReply
instance r (whose getError() is null) causing an NPE and loses the real cause;
replace r.getError() usages with reply.getError() when calling err(...) so the
actual error and details from reply are propagated (i.e., build the error with
reply.getError() / reply.getError().getDetails()), and then bus.reply(msg, r) as
before; optionally add a null-check on reply.getError() and set a generic error
if it's unexpectedly null.

Comment on lines 99 to 107
if (!service.getExternalServiceCapabilities().isReloadConfig()) {
event.setError(operr("service[%s] does not support reload config", msg.getName()));
event.setError(operr(ORG_ZSTACK_CORE_EXTERNALSERVICE_10002, "service[%s] does not support reload config", msg.getName()));
}

if (service.isAlive()) {
service.reload();
} else {
event.setError(operr("service[%s] is not running", msg.getName()));
event.setError(operr(ORG_ZSTACK_CORE_EXTERNALSERVICE_10003, "service[%s] is not running", msg.getName()));
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

逻辑缺陷:缺少 early return 导致错误行为。

当服务不支持 reload config 时(第 100 行设置错误后),代码没有提前返回,而是继续执行后续逻辑:

  1. 如果服务存活,会错误地调用 service.reload()
  2. 如果服务未运行,错误码会被 ORG_ZSTACK_CORE_EXTERNALSERVICE_10003 覆盖

根据编码规范,应使用 early return 模式。

🐛 修复建议:添加 early return
 if (!service.getExternalServiceCapabilities().isReloadConfig()) {
     event.setError(operr(ORG_ZSTACK_CORE_EXTERNALSERVICE_10002, "service[%s] does not support reload config", msg.getName()));
+    bus.publish(event);
+    return;
 }

 if (service.isAlive()) {
     service.reload();
 } else {
     event.setError(operr(ORG_ZSTACK_CORE_EXTERNALSERVICE_10003, "service[%s] is not running", msg.getName()));
 }
🤖 Prompt for AI Agents
In
@core/src/main/java/org/zstack/core/externalservice/ExternalServiceManagerImpl.java
around lines 99 - 107, In ExternalServiceManagerImpl, after checking
service.getExternalServiceCapabilities().isReloadConfig() and calling
event.setError(...) when reload is not supported, add an early return so
execution does not continue to the subsequent service.isAlive() check and
service.reload() call; ensure the branch that sets
ORG_ZSTACK_CORE_EXTERNALSERVICE_10002 exits immediately (preventing
service.reload() from being invoked and preventing the error from being
overwritten by ORG_ZSTACK_CORE_EXTERNALSERVICE_10003).

import org.zstack.header.errorcode.OperationFailureException;

import static org.zstack.core.Platform.operr;
import static org.zstack.utils.clouderrorcode.CloudOperationsErrorCode.*;
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

rg -n "ORG_ZSTACK_KVM_10011" --type=java -C 2

Repository: MatheMatrix/zstack

Length of output: 180


🏁 Script executed:

# Find and examine CloudOperationsErrorCode file
fd -n "CloudOperationsErrorCode.java" --type f

Repository: MatheMatrix/zstack

Length of output: 180


🔍 Additional research query:

Search DevinDeepWiki for MatheMatrix/zstack repository documentation about CloudOperationsErrorCode and error code ORG_ZSTACK_KVM_10011

💡 Result:

Perfect! I've gathered comprehensive information about the error code system in the MatheMatrix/zstack repository. Let me provide you with the findings:

CloudOperationsErrorCode and Error Code ORG_ZSTACK_KVM_10011

Error Code Structure The ZStack repository uses an XML-based error code system where each error has a category, code, and description. These structured error codes are processed by the Platform.operr() method to create ErrorCode objects, which are then converted into user-friendly, localized messages using templates defined in Elaboration.json.

Important note: CloudOperationsErrorCode is not explicitly defined as a class or enum in the codebase. Instead, the system uses ErrorCode objects generated by the error handling infrastructure.

ORG_ZSTACK_KVM_10011 Error Code The error code ORG_ZSTACK_KVM_10011 represents a "Connection error for KVM host".

Details:

  • English Message: "Connection error for KVM host [uuid: %1$s, ip: %2$s]."
  • Chinese Message: "物理机 [uuid:%1$s,ip:%2$s] 连接错误。"
  • Error Category: HOST (derived from category + id 1011)
  • Trigger Condition: Occurs when there is a connection issue with a KVM host

The error elaboration system uses pattern matching (regex and Jaro-Winkler similarity) to match error messages against predefined templates and return the appropriate localized message based on the system's locale.


🔍 Additional research query:

Search AtlassianMCP for Jira issues related to MatheMatrix zstack PR 2763 or error code definitions

💡 Result:

Search Results Summary

I conducted a comprehensive search of AtlassianMCP Jira for issues related to your query. Here are the findings:

MatheMatrix ZStack PR 2763

Result: No specific Jira issues found directly linking "MatheMatrix," "zstack," and "PR 2763" together. The searches returned general ZStack-related issues but nothing specific to PR 2763 or a MatheMatrix pull request.

Related MatheMatrix & ZStack Issues

The search did identify that MatheMatrix is mentioned in ZStack project issues:

  • ZSTAC-2861: "support vxlan for ZStack" - Description mentions collaboration with MatheMatrix user regarding vxlan functionality for ZStack (Status: Closed)

Error Code Definitions

The search for error code definitions returned 1,633 total issues with many relevant results, including:

  • ZSTAC-21399: "error_code" (Resolved)
  • Multiple "[ERROR CODE]" and "[Error code]" titled issues covering various system errors, operational issues, and connection problems
  • Issues span from 2019-2023, primarily in the ZSTAC project

Note: The specific PR 2763 reference did not match directly in Jira summaries or descriptions. This could indicate:

  1. The PR may be referenced in GitHub rather than Jira
  2. It may be tracked under a different naming convention
  3. It may not have a corresponding Jira issue

错误码定义与使用存在不匹配,需要更正。

虽然 ORG_ZSTACK_KVM_10011 确实在 ZStack 错误码系统中定义,但其语义为 "Connection error for KVM host [uuid: %1$s, ip: %2$s]"(物理机连接错误),与代码实际使用场景不符。该代码在 line 173 用于验证 forceRun 时必须提供 reason("the reason must be stated when setting force run"),这是一个参数验证错误,而非连接错误。

需要使用正确的错误码来匹配实际的验证场景,或创建新的错误码定义来适配此验证逻辑。

if(!(LdapConstant.OpenLdap.TYPE.equals(type) || LdapConstant.WindowsAD.TYPE.equals(type))){
throw new ApiMessageInterceptionException(
argerr("Wrong LdapServerType[%s], valid values: [%,%s]", type, LdapConstant.OpenLdap.TYPE, LdapConstant.WindowsAD.TYPE)
argerr(ORG_ZSTACK_LDAP_10010, "Wrong LdapServerType[%s], valid values: [%,%s]", type, LdapConstant.OpenLdap.TYPE, LdapConstant.WindowsAD.TYPE)
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

格式字符串存在 bug。

"[%,%s]" 缺少第一个 % 后的格式说明符 s,会导致运行时格式化错误或输出异常。应该是 "[%s,%s]"

🐛 修复格式字符串错误
-                    argerr(ORG_ZSTACK_LDAP_10010, "Wrong LdapServerType[%s], valid values: [%,%s]", type, LdapConstant.OpenLdap.TYPE, LdapConstant.WindowsAD.TYPE)
+                    argerr(ORG_ZSTACK_LDAP_10010, "Wrong LdapServerType[%s], valid values: [%s,%s]", type, LdapConstant.OpenLdap.TYPE, LdapConstant.WindowsAD.TYPE)
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
argerr(ORG_ZSTACK_LDAP_10010, "Wrong LdapServerType[%s], valid values: [%,%s]", type, LdapConstant.OpenLdap.TYPE, LdapConstant.WindowsAD.TYPE)
argerr(ORG_ZSTACK_LDAP_10010, "Wrong LdapServerType[%s], valid values: [%s,%s]", type, LdapConstant.OpenLdap.TYPE, LdapConstant.WindowsAD.TYPE)
🤖 Prompt for AI Agents
In @plugin/ldap/src/main/java/org/zstack/ldap/LdapApiInterceptor.java at line
129, The format string in the argerr call inside LdapApiInterceptor is wrong
("[%,%s]") and will cause runtime formatting errors; update the call to use a
correct format string "[%s,%s]" and ensure the arguments remain (type,
LdapConstant.OpenLdap.TYPE, LdapConstant.WindowsAD.TYPE) so the error message
produced by argerr(ORG_ZSTACK_LDAP_10010, ...) correctly prints the provided
type and the two valid constants.

Comment on lines 116 to 119
if (!q(PrimaryStorageClusterRefVO.class).eq(PrimaryStorageClusterRefVO_.primaryStorageUuid, psUuid).isExists()) {
throw new OperationFailureException(operr("the NFS primary storage[uuid:%s] is not attached" +
throw new OperationFailureException(operr(ORG_ZSTACK_STORAGE_PRIMARY_NFS_10000, "the NFS primary storage[uuid:%s] is not attached" +
" to any clusters, and cannot expunge the root volume[uuid:%s] of the VM[uuid:%s]", psUuid, vmUuid, volumeUuid));
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

格式化参数顺序错误

错误消息中的参数顺序不正确。消息模板期望的顺序是 psUuid, volumeUuid, vmUuid,但实际传入的是 psUuid, vmUuid, volumeUuid,导致 root volume UUID 和 VM UUID 在错误消息中会互换显示。

🐛 修复参数顺序
 if (!q(PrimaryStorageClusterRefVO.class).eq(PrimaryStorageClusterRefVO_.primaryStorageUuid, psUuid).isExists()) {
-    throw new OperationFailureException(operr(ORG_ZSTACK_STORAGE_PRIMARY_NFS_10000, "the NFS primary storage[uuid:%s] is not attached" +
-            " to any clusters, and cannot expunge the root volume[uuid:%s] of the VM[uuid:%s]", psUuid, vmUuid, volumeUuid));
+    throw new OperationFailureException(operr(ORG_ZSTACK_STORAGE_PRIMARY_NFS_10000, "the NFS primary storage[uuid:%s] is not attached" +
+            " to any clusters, and cannot expunge the root volume[uuid:%s] of the VM[uuid:%s]", psUuid, volumeUuid, vmUuid));
 }
🤖 Prompt for AI Agents
In
@plugin/nfsPrimaryStorage/src/main/java/org/zstack/storage/primary/nfs/NfsPrimaryStorageFactory.java
around lines 116 - 119, The error message in NfsPrimaryStorageFactory throws an
OperationFailureException using operr(ORG_ZSTACK_STORAGE_PRIMARY_NFS_10000,
"...%s) with parameters passed as psUuid, vmUuid, volumeUuid which mismatches
the template order (psUuid, volumeUuid, vmUuid); fix by swapping the last two
arguments so the operr call passes psUuid, volumeUuid, vmUuid (update the throw
in NfsPrimaryStorageFactory where OperationFailureException is created).

import java.util.*;

import static org.zstack.core.Platform.operr;
import static org.zstack.utils.clouderrorcode.CloudOperationsErrorCode.*;
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Find CloudOperationsErrorCode.java
fd -t f "CloudOperationsErrorCode.java"

Repository: MatheMatrix/zstack

Length of output: 143


🏁 Script executed:

#!/bin/bash
# Check the specific lines in SdnControllerManagerImpl.java to see the error messages
FILE="plugin/sdnController/src/main/java/org/zstack/sdnController/SdnControllerManagerImpl.java"
if [ -f "$FILE" ]; then
  echo "=== Lines around 472, 499, 624, 680 ==="
  sed -n '470,475p;497,502p;622,627p;678,683p' "$FILE"
else
  echo "File not found: $FILE"
fi

Repository: MatheMatrix/zstack

Length of output: 1367


🏁 Script executed:

#!/bin/bash
# Check for the import statement at line 45
FILE="plugin/sdnController/src/main/java/org/zstack/sdnController/SdnControllerManagerImpl.java"
if [ -f "$FILE" ]; then
  echo "=== Imports section (lines 40-50) ==="
  sed -n '40,50p' "$FILE"
fi

Repository: MatheMatrix/zstack

Length of output: 679


🏁 Script executed:

#!/bin/bash
# Search for CloudOperationsErrorCode and verify constants
rg "ORG_ZSTACK_SDNCONTROLLER_" --type java -n

Repository: MatheMatrix/zstack

Length of output: 39612


修正错误消息中的英文语法错误。

静态导入 CloudOperationsErrorCode 的用法正确,且所有引用的错误码常量(ORG_ZSTACK_SDNCONTROLLER_1000010008)已在 CloudOperationsErrorCode.java 中正确定义。

但需要修正以下位置的错误消息中的英文语法错误:

第 472、499、624、680 行:错误消息 "sdn l2 network[uuid:%s] is not attached controller" 的语法不正确。应改为 "sdn l2 network[uuid:%s] is not attached to a controller""sdn l2 network[uuid:%s] does not have an attached controller",以符合编码规范对正确英文的要求。

@MatheMatrix MatheMatrix force-pushed the sync/lin.ma/5.5.6_global_error_code@@2 branch from 2358a21 to 8fba7f4 Compare January 12, 2026 02:04
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 10

Note

Due to the large number of review comments, Critical, Major severity comments were prioritized as inline comments.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (21)
compute/src/main/java/org/zstack/compute/vm/MacOperator.java (1)

84-96: 错误消息缺少 MAC 地址上下文

第 91 行的错误消息 "Disallowed address" 与同方法中其他错误消息不一致。第 88 行和第 94 行都包含了具体的 MAC 地址以便于调试,建议此处也保持一致。

建议的修复
         if ("00:00:00:00:00:00".equals(lowercaseMac) || "ff:ff:ff:ff:ff:ff".equals(lowercaseMac)) {
-            throw new OperationFailureException(operr(ORG_ZSTACK_COMPUTE_VM_10227, "Disallowed address"));
+            throw new OperationFailureException(operr(ORG_ZSTACK_COMPUTE_VM_10227, "Disallowed MAC address [%s]", mac));
         }
compute/src/main/java/org/zstack/compute/host/HostApiInterceptor.java (1)

129-137: 错误消息存在轻微语法问题。

第 135 行的错误消息 "can not maintain host[uuid:%s, status:%s]which is not Connected"]which 之间缺少空格。

根据编码规范,代码中应使用正确的英文。

建议修复
-            throw new ApiMessageInterceptionException(operr(ORG_ZSTACK_COMPUTE_HOST_10114, "can not maintain host[uuid:%s, status:%s]which is not Connected", msg.getHostUuid(), hostStatus));
+            throw new ApiMessageInterceptionException(operr(ORG_ZSTACK_COMPUTE_HOST_10114, "cannot maintain host[uuid:%s, status:%s] which is not Connected", msg.getHostUuid(), hostStatus));

另外,"can not" 应写作 "cannot"。

plugin/eip/src/main/java/org/zstack/network/service/eip/EipApiInterceptor.java (2)

119-131: 错误消息与逻辑不符。

found == false 时抛出此异常,说明 IP 关联到 vmNic,但错误消息却写的是 "is attached"(已关联)。这会误导排查问题的人员。

🐛 修复建议
         if (!found) {
-            throw new ApiMessageInterceptionException(argerr(ORG_ZSTACK_NETWORK_SERVICE_EIP_10007, "ip [uuid:%s] is attached to vm nic [%s]", guestIpUuid, vmNicUuid));
+            throw new ApiMessageInterceptionException(argerr(ORG_ZSTACK_NETWORK_SERVICE_EIP_10007, "ip [uuid:%s] is not attached to vm nic [%s]", guestIpUuid, vmNicUuid));
         }

191-205: 错误消息参数错误。

Line 200 的错误消息格式为 "Ip address [uuid:%s] is not belonged to nic [uuid:%s]",但传入的第一个参数是 msg.getEipUuid() 而非 msg.getUsedIpUuid()。这会导致日志输出错误的 UUID。

🐛 修复建议
             if (!found) {
-                throw new ApiMessageInterceptionException(argerr(ORG_ZSTACK_NETWORK_SERVICE_EIP_10012, "Ip address [uuid:%s] is not belonged to nic [uuid:%s]", msg.getEipUuid(), msg.getVmNicUuid()));
+                throw new ApiMessageInterceptionException(argerr(ORG_ZSTACK_NETWORK_SERVICE_EIP_10012, "Ip address [uuid:%s] is not belonged to nic [uuid:%s]", msg.getUsedIpUuid(), msg.getVmNicUuid()));
             }
compute/src/main/java/org/zstack/compute/vm/CpuTopology.java (1)

71-79: 应使用 ApiMessageInterceptionException 而非 OperationFailureException

根据 ZStack VM 系统的异常处理规范,参数验证逻辑应抛出 ApiMessageInterceptionException 以保持一致性。当前代码使用 OperationFailureException 不符合要求。

建议修改为:

if (throwException) {
    throw new ApiMessageInterceptionException(operr(ORG_ZSTACK_COMPUTE_VM_10224, "cpu topology is not correct, cpuNum[%s], configured cpuSockets[%s], cpuCores[%s], cpuThreads[%s];" +
                    " Calculated cpuSockets[%s], cpuCores[%s], cpuThreads[%s]",
            cpuNum, cpuSockets, cpuCores, cpuThreads, socketNum, coreNum, threadNum));
} else {
    return String.format("cpu topology is not correct, cpuNum[%s], configured cpuSockets[%s], cpuCores[%s], cpuThreads[%s];" +
                    " Calculated cpuSockets[%s], cpuCores[%s], cpuThreads[%s]",
            cpuNum, cpuSockets, cpuCores, cpuThreads, socketNum, coreNum, threadNum);
}
plugin/loadBalancer/src/main/java/org/zstack/network/service/lb/LoadBalancerManagerImpl.java (1)

715-744: 修复错误消息中的重复单词 "invalid"。

第 737 行和第 740 行的错误消息中包含重复的 "invalid" 单词:"invalid invalid health target" 应改为 "invalid health target"。根据编码规范,代码应使用正确、无拼写错误的英文。

🐛 建议修复
                    if (p < 1 || p > 65535) {
-                        throw new OperationFailureException(argerr(ORG_ZSTACK_NETWORK_SERVICE_LB_10015, "invalid invalid health target[%s], port[%s] is not in the range of [1, 65535]", systemTag, port));
+                        throw new OperationFailureException(argerr(ORG_ZSTACK_NETWORK_SERVICE_LB_10015, "invalid health target[%s], port[%s] is not in the range of [1, 65535]", systemTag, port));
                    }
                } catch (NumberFormatException e) {
-                    throw new OperationFailureException(argerr(ORG_ZSTACK_NETWORK_SERVICE_LB_10016, "invalid invalid health target[%s], port[%s] is not a number", systemTag, port));
+                    throw new OperationFailureException(argerr(ORG_ZSTACK_NETWORK_SERVICE_LB_10016, "invalid health target[%s], port[%s] is not a number", systemTag, port));
                }
plugin/loadBalancer/src/main/java/org/zstack/network/service/lb/LoadBalancerApiInterceptor.java (3)

254-287: 修复拼写错误:"cloud" 应为 "could"。

第 280 行和第 284 行的错误消息中 "cloud not create loadbalancer" 应改为 "could not create loadbalancer"。根据编码规范,代码应使用正确、无拼写错误的英文。

🐛 建议修复
            } else {
-                throw new ApiMessageInterceptionException(argerr(ORG_ZSTACK_NETWORK_SERVICE_LB_10035, "cloud not create loadbalancer, because param vipUuid point to VIP[%s] is not ipv4 VIP", vipVO.getUuid()));
+                throw new ApiMessageInterceptionException(argerr(ORG_ZSTACK_NETWORK_SERVICE_LB_10035, "could not create loadbalancer, because param vipUuid point to VIP[%s] is not ipv4 VIP", vipVO.getUuid()));
            }
        } else if (IPv6Constants.IPv6 == vipIpVersion) {
            if (!IPv6NetworkUtils.isIpv6Address(vipVO.getIp())) {
-                throw new ApiMessageInterceptionException(argerr(ORG_ZSTACK_NETWORK_SERVICE_LB_10036, "cloud not create loadbalancer, because param ipv6VipUuid point to VIP[%s] is not ipv6 VIP", vipVO.getUuid()));
+                throw new ApiMessageInterceptionException(argerr(ORG_ZSTACK_NETWORK_SERVICE_LB_10036, "could not create loadbalancer, because param ipv6VipUuid point to VIP[%s] is not ipv6 VIP", vipVO.getUuid()));
            }
        }

1031-1136: 修复多处拼写错误:"cloud" 应为 "could"。

多个错误消息中存在 "cloud not create/change the loadbalancer listener" 的拼写错误,应改为 "could not"。涉及行号:1076、1082、1097、1102、1118、1124。根据编码规范,代码应使用正确、无拼写错误的英文。

🐛 建议修复(部分示例)
            if (!LoadBalancerConstants.LB_PROTOCOL_HTTPS.equals(msg.getProtocol())) {
                throw new ApiMessageInterceptionException(
-                        argerr(ORG_ZSTACK_NETWORK_SERVICE_LB_10090, "cloud not change the loadbalancer listener, because the listener with protocol [%s] doesn't support select http version:[%s]",
+                        argerr(ORG_ZSTACK_NETWORK_SERVICE_LB_10090, "could not change the loadbalancer listener, because the listener with protocol [%s] doesn't support select http version:[%s]",
                                msg.getProtocol(), msg.getHttpVersions()));
            }

            if (hasNotSupportedHttpVersion(msg.getHttpVersions())) {
                throw new ApiMessageInterceptionException(
-                        argerr(ORG_ZSTACK_NETWORK_SERVICE_LB_10091, "cloud not create the loadbalancer listener, because the listener with protocol https only support http version:[h1, h2]"));
+                        argerr(ORG_ZSTACK_NETWORK_SERVICE_LB_10091, "could not create the loadbalancer listener, because the listener with protocol https only support http version:[h1, h2]"));
            }

类似地修复以下行中的 "cloud" -> "could":1097、1102、1118、1124


1183-1402: 修复多处拼写错误:"cloud" 应为 "could"。

APIChangeLoadBalancerListenerMsg 验证方法中多个错误消息存在 "cloud not change the loadbalancer listener" 的拼写错误。涉及行号:1365、1371、1378、1383、1390、1396。

🐛 建议修复(部分示例)
        if (!CollectionUtils.isEmpty(msg.getHttpVersions())) {
            if (!LoadBalancerConstants.LB_PROTOCOL_HTTPS.equals(listener.getProtocol())) {
                throw new ApiMessageInterceptionException(
-                        argerr(ORG_ZSTACK_NETWORK_SERVICE_LB_10122, "cloud not change the loadbalancer listener, because the listener with protocol [%s] doesn't support select http version:[%s]",
+                        argerr(ORG_ZSTACK_NETWORK_SERVICE_LB_10122, "could not change the loadbalancer listener, because the listener with protocol [%s] doesn't support select http version:[%s]",
                                listenerVO.getProtocol(), msg.getHttpVersions()));
            }

类似地修复以下行中的 "cloud" -> "could":1371、1378、1383、1390、1396

network/src/main/java/org/zstack/network/l3/L3NetworkApiInterceptor.java (2)

179-187: 格式化字符串与参数数量不匹配。

格式化字符串 "[%s:%s] is overlapped with old range" 只有 2 个占位符,但传递了 4 个参数。旧范围的起始和结束 IP 未在消息中显示。

🐛 建议修复
                 if (NetworkUtils.isIpv4RangeOverlap(msg.getStartIp(), msg.getEndIp(),
                         reserveRange.getStartIp(), reserveRange.getEndIp())) {
                     throw new ApiMessageInterceptionException(argerr(ORG_ZSTACK_NETWORK_L3_10008, "could not reserve ip range, " +
-                            "because new range [%s:%s] is overlapped with old range",
-                            msg.getStartIp(), msg.getEndIp(),
-                            reserveRange.getStartIp(), reserveRange.getEndIp()));
+                            "because new range [%s:%s] is overlapped with old range [%s:%s]",
+                            msg.getStartIp(), msg.getEndIp(), reserveRange.getStartIp(), reserveRange.getEndIp()));
                 }

208-216: 格式化字符串与参数数量不匹配。

与上述 IPv4 范围重叠检查相同的问题,IPv6 范围重叠检查的格式化字符串也缺少旧范围的占位符。

🐛 建议修复
                 if (IPv6NetworkUtils.isIpv6RangeOverlap(msg.getStartIp(), msg.getEndIp(),
                         reserveRange.getStartIp(), reserveRange.getEndIp())) {
                     throw new ApiMessageInterceptionException(argerr(ORG_ZSTACK_NETWORK_L3_10011, "could not reserve ip range, " +
-                                    "because new range [%s:%s] is overlapped with old range",
-                            msg.getStartIp(), msg.getEndIp(),
-                            reserveRange.getStartIp(), reserveRange.getEndIp()));
+                                    "because new range [%s:%s] is overlapped with old range [%s:%s]",
+                            msg.getStartIp(), msg.getEndIp(), reserveRange.getStartIp(), reserveRange.getEndIp()));
                 }
header/src/main/java/org/zstack/header/errorcode/ErrorCode.java (1)

76-84: 拷贝构造函数遗漏了 globalErrorCode 字段。

当使用此拷贝构造函数创建 ErrorCode 副本时,globalErrorCode 不会被复制,可能导致错误码信息在传播链中丢失。

🔧 建议修复
 public ErrorCode(ErrorCode other) {
     this.code = other.code;
     this.description = other.description;
     this.details = other.details;
     this.elaboration = other.elaboration;
     this.messages = other.messages;
     this.cause = other.cause;
     this.location = other.location;
+    this.globalErrorCode = other.globalErrorCode;
 }
compute/src/main/java/org/zstack/compute/vm/VmInstanceBase.java (3)

2895-2924: 修复:AttachNicToVmReply 的 setError 使用了未初始化的 r.getError()(会 NPE)。
当前 r 是新建的 AttachNicToVmReplyr.getError() 为 null;应使用回包 reply.getError() 作为 cause。

建议修复(最小 diff)
-                AttachNicToVmReply r = new AttachNicToVmReply();
+                AttachNicToVmReply r = new AttachNicToVmReply();
                 if (!reply.isSuccess()) {
-                    r.setError(err(ORG_ZSTACK_COMPUTE_VM_10274, VmErrors.ATTACH_NETWORK_ERROR, r.getError(), r.getError().getDetails()));
+                    r.setError(err(ORG_ZSTACK_COMPUTE_VM_10274, VmErrors.ATTACH_NETWORK_ERROR,
+                            reply.getError(), reply.getError().getDetails()));
                 }
                 bus.reply(msg, r);

5765-5819: 修复:updateVmIsoFirstOrder() 把 protocol 写进了 isoInstallPath。
sourceCdRomVO.setIsoInstallPath(protocol) 明显是字段写错,导致协议丢失/路径被污染。

建议修复(最小 diff)
         sourceCdRomVO.setIsoUuid(targetCdRomIsoUuid);
         sourceCdRomVO.setIsoInstallPath(path);
-        sourceCdRomVO.setIsoInstallPath(protocol);
+        sourceCdRomVO.setProtocol(protocol);

6685-6700: 错误码语义丢失且缺少 cause 链:operr() 应改为 err() 并保留原错误码

validateOperationByState() 返回的 ErrorCode 包含 VmErrors.DETACH_VOLUME_ERROR,但当前代码直接使用 operr() 创建新的 ErrorCode(底层用 SysErrors.OPERATION_ERROR),丢弃了原错误码及 cause 信息。这会影响上层按错误码进行分支处理和错误统计。

建议改为:

修复方案
         ErrorCode allowed = validateOperationByState(msg, self.getState(), VmErrors.DETACH_VOLUME_ERROR);
         if (allowed != null) {
-            completion.fail(operr(ORG_ZSTACK_COMPUTE_VM_10284, "Detaching volume is not allowed when VM[uuid=%s] is in state[%s]",
-                    self.getUuid(), self.getState()));
+            completion.fail(err(ORG_ZSTACK_COMPUTE_VM_10284, VmErrors.DETACH_VOLUME_ERROR, allowed,
+                    "Detaching volume is not allowed when VM[uuid=%s] is in state[%s]",
+                    self.getUuid(), self.getState()));
             return;
         }
compute/src/main/java/org/zstack/compute/vm/VmInstanceApiInterceptor.java (2)

325-367: Static IP 占用校验使用了错误的 l3/ip 变量(会误判/漏判)

在遍历 staticIps 时,占用检查却用的是 msg.getDestL3NetworkUuid()/msg.getStaticIp(),而不是当前循环的 l3Uuid/staticIp。这是明确的逻辑 bug。

建议修复
                 SimpleQuery<UsedIpVO> uq = dbf.createQuery(UsedIpVO.class);
-                uq.add(UsedIpVO_.l3NetworkUuid, Op.EQ, msg.getDestL3NetworkUuid());
-                uq.add(UsedIpVO_.ip, Op.EQ, msg.getStaticIp());
+                uq.add(UsedIpVO_.l3NetworkUuid, Op.EQ, l3Uuid);
+                uq.add(UsedIpVO_.ip, Op.EQ, staticIp);
                 if (uq.isExists()) {
                     throw new ApiMessageInterceptionException(operr(ORG_ZSTACK_COMPUTE_VM_10108, "the static IP[%s] has been occupied on the L3 network[uuid:%s]", staticIp, l3Uuid));
                 }

387-405: Interceptor 校验里抛 OperationFailureException:建议统一为 ApiMessageInterceptionException

这里属于 API 参数/前置条件校验,放在 ApiInterceptor 中更建议抛 ApiMessageInterceptionException,避免上层处理分支不一致(基于 learnings)。

建议修复
-                    throw new OperationFailureException(argerr(ORG_ZSTACK_COMPUTE_VM_10112, "the image[name:%s, uuid:%s] is an ISO, rootDiskSize must be set",
-                            image.getName(), image.getUuid()));
+                    throw new ApiMessageInterceptionException(argerr(ORG_ZSTACK_COMPUTE_VM_10112, "the image[name:%s, uuid:%s] is an ISO, rootDiskSize must be set",
+                            image.getName(), image.getUuid()));
plugin/ceph/src/main/java/org/zstack/storage/ceph/primary/CephPrimaryStorageBase.java (2)

6166-6178: 补齐 allocatedUrl 的结构校验,避免 lastIndexOf("/") == -1 时抛出非预期异常。
现在只校验了 nullceph:// 前缀;若 allocatedUrl 形如 ceph://poolOnlysubstring(0, -1) 会直接炸。

建议修改
         String path = allocatedUrl.replaceFirst("ceph://", "");
-        return path.substring(0, path.lastIndexOf("/"));
+        int idx = path.lastIndexOf("/");
+        if (idx < 0) {
+            throw new OperationFailureException(operr(ORG_ZSTACK_STORAGE_CEPH_PRIMARY_10033, "invalid allocated url:%s", allocatedUrl));
+        }
+        return path.substring(0, idx);

96-102: 常量与方法签名验证已通过,但需修复英文拼写错误

验证结果:

  • CloudOperationsErrorCodeORG_ZSTACK_STORAGE_CEPH_PRIMARY_10000ORG_ZSTACK_STORAGE_CEPH_PRIMARY_10033 等常量均已存在 ✓
  • Platform.operr/err 方法重载签名正确存在 ✓

但以下拼写错误需修正(违反编码规范"报错应使用正确的、无拼写错误的英文"):

  • 第 5059 行"depreciated" → 改为 "deprecated"
  • 第 4391 行"cep cluster" → 改为 "ceph cluster"
  • 第 5274、5301 行:错误消息中的 "rootvolume""imagecache""backupstorage""primarystorage" 应改为带分隔符的形式(如 "root volume""image cache" 等)

此外,建议检查 getTargetPoolNameFromAllocatedUrl() 方法:若移除前缀后的路径不包含 "/"lastIndexOf("/") 返回 -1 时可能导致 StringIndexOutOfBoundsException

plugin/kvm/src/main/java/org/zstack/kvm/KVMHostFactory.java (1)

780-792: 格式化字符串缺少占位符。

第 790 行的错误消息 "pci bridge need a value greater than 0 and lower than 32" 没有 %s 占位符,但传入了 KVMSystemTags.VM_PREDEFINED_PCI_BRIDGE_NUM_TOKEN 参数,该参数将被忽略。应修复格式化字符串以包含参数引用,保持与第 786 行的一致性。

🐛 建议修复格式化字符串
             if (num <= 0 || num > 31) {
-                throw new ApiMessageInterceptionException(argerr(ORG_ZSTACK_KVM_10008, "pci bridge need a value greater than 0 and lower than 32", KVMSystemTags.VM_PREDEFINED_PCI_BRIDGE_NUM_TOKEN));
+                throw new ApiMessageInterceptionException(argerr(ORG_ZSTACK_KVM_10008, "%s must be greater than 0 and less than 32", KVMSystemTags.VM_PREDEFINED_PCI_BRIDGE_NUM_TOKEN));
             }
plugin/kvm/src/main/java/org/zstack/kvm/KVMHost.java (1)

6303-6310: **超时错误信息单位不一致:提示 seconds 但传了毫秒值。**Line 6305 目前把 ctimeout(毫秒)填进了 “%d seconds”,会误导排障。

建议修改
-                long timeoutInSec = KVMConstant.KVM_HOST_POWER_OPERATION_TIMEOUT_SECONDS;
-                long ctimeout = TimeUnit.SECONDS.toMillis(timeoutInSec);
+                long timeoutInSec = KVMConstant.KVM_HOST_POWER_OPERATION_TIMEOUT_SECONDS;
+                long ctimeout = TimeUnit.SECONDS.toMillis(timeoutInSec);
                 Long deadline = timeHelper.getCurrentTimeMillis() + ctimeout;
                 thdf.submitCancelablePeriodicTask(new CancelablePeriodicTask(msg, completion) {
@@
                             if (!msg.isReturnEarly()) {
                                 reply.setSuccess(false);
-                                reply.setError(operr(ORG_ZSTACK_KVM_10123, "host[%s] not shutdown in %d seconds", msg.getHostUuid(), ctimeout));
+                                reply.setError(operr(ORG_ZSTACK_KVM_10123, "host[%s] not shut down in %d seconds", msg.getHostUuid(), timeoutInSec));
                                 bus.reply(msg, reply);
                                 noErrorCompletion.done();
                             }
🤖 Fix all issues with AI agents
In @compute/src/main/java/org/zstack/compute/vm/VmAllocateNicFlow.java:
- Around line 113-117: In VmAllocateNicFlow, when type == null the code adds an
error via errs.add(...) and calls wcomp.allDone() but does not return, so
vmMgr.getVmInstanceNicFactory(type) is invoked with a null type; fix by adding
an immediate return after wcomp.allDone() (i.e., ensure the control flow exits
the method/closure right after wcomp.allDone() to prevent calling
vmMgr.getVmInstanceNicFactory(type) with null).

In
@console/src/main/java/org/zstack/console/ManagementServerConsoleProxyBackend.java:
- Line 571: The error message invocation to
trigger.fail(operr(ORG_ZSTACK_CONSOLE_10003, "failed to configure
consoleProxyOverriddenIp[code:%d] or consoleProxyPort[code:%d]")) is missing the
formatting arguments; update the call to pass the actual status variables (rst
and portRst) into operr so the two %d placeholders are filled (e.g.,
operr(ORG_ZSTACK_CONSOLE_10003, "failed to configure
consoleProxyOverriddenIp[code:%d] or consoleProxyPort[code:%d]", rst, portRst));
locate this in ManagementServerConsoleProxyBackend where trigger.fail(...) is
invoked and ensure rst and portRst are in scope and used as the format
parameters.

In @identity/src/main/java/org/zstack/identity/DefaultAuthorizationBackend.java:
- Line 67: In DefaultAuthorizationBackend replace the thrown
OperationFailureException with an ApiMessageInterceptionException at the API
message interception permission check, correct the error text to "the
operations[%s] are denied", and swap the erroneous ORG_ZSTACK_IDENTITY_10005
error code for the proper permission-denial error code (e.g., PERMISSION_CODE or
the project’s designated permission denial constant) so the exception and
message match the interception/permission semantics.

In @network/src/main/java/org/zstack/network/l3/L3NetworkApiInterceptor.java:
- Around line 141-144: In L3NetworkApiInterceptor, the validation for
msg.getEndIp() incorrectly reports "start ip" and uses msg.getStartIp() in the
ApiMessageInterceptionException; update the exception to mention "end ip" and
pass msg.getEndIp() to argerr (ORG_ZSTACK_NETWORK_L3_10002) so the error message
and variable match the checked field.
- Around line 798-809: In validate(APIRemoveHostRouteFromL3NetworkMsg msg) the
first ApiMessageInterceptionException formats the error with the wrong variable
(uses msg.getL3NetworkUuid()); change the formatted argument to msg.getPrefix()
so the argerr call reads "prefix [%s] is not a IPv4 network cidr" with
msg.getPrefix() to correctly report the invalid prefix.
- Around line 781-795: In validate(APIAddHostRouteToL3NetworkMsg msg) in
L3NetworkApiInterceptor the first argerr uses the wrong variable: it validates
msg.getPrefix() but logs msg.getL3NetworkUuid(); change the error message to
include msg.getPrefix() (i.e., replace msg.getL3NetworkUuid() with
msg.getPrefix()) so the exception reports the invalid CIDR value being checked.

In
@plugin/ceph/src/main/java/org/zstack/storage/ceph/primary/CephPrimaryStorageBase.java:
- Around line 1799-1803: The bug is using a dynamic user-provided string
(details from trash.makeSureInstallPathNotUsed) directly as the format/template
argument to operr (in CephPrimaryStorageBase around the block that builds
TrashCleanupResult and the similar block at ~1894-1899), which can throw
MissingFormatArgumentException if details contains % sequences; fix by passing a
literal format string and the details as an argument (e.g., use "%s" as the
template) when calling operr so the details are treated as data, not a format
string.

In
@plugin/flatNetworkProvider/src/main/java/org/zstack/network/service/flat/FlatDhcpBackend.java:
- Line 2182: The operr call in FlatDhcpBackend uses rsp.getError() twice; remove
the redundant argument so the call matches the expected signature (e.g.,
operr(ORG_ZSTACK_NETWORK_SERVICE_FLAT_10028, rsp.getError(), "operation error,
because:%s")) — update the operr invocation that currently uses
ORG_ZSTACK_NETWORK_SERVICE_FLAT_10028 and rsp to drop the duplicate
rsp.getError() parameter.

In @plugin/ldap/src/main/java/org/zstack/ldap/LdapApiInterceptor.java:
- Around line 127-131: The format string in LdapApiInterceptor's type validation
uses a malformed placeholder "[%,%s]" causing incorrect formatting; update the
argerr call (the ApiMessageInterceptionException construction) to use the
correct placeholder sequence "[%s,%s]" so the error message includes the
provided type and the two valid constants (LdapConstant.OpenLdap.TYPE and
LdapConstant.WindowsAD.TYPE) when throwing with ORG_ZSTACK_LDAP_10010.
🟡 Minor comments (33)
network/src/main/java/org/zstack/network/l3/L3NetworkCascadeExtension.java-32-34 (1)

32-34: 导入语句格式错误:/** 被错误地附加到了导入声明上。

这行代码存在格式问题,/** 不应该直接跟在导入语句后面。这可能是合并代码时产生的问题,会导致代码格式混乱。

🔧 建议修复
-import static org.zstack.utils.clouderrorcode.CloudOperationsErrorCode.*;/**
- */
-
+import static org.zstack.utils.clouderrorcode.CloudOperationsErrorCode.*;
+
+/**
+ */
plugin/nfsPrimaryStorage/src/main/java/org/zstack/storage/primary/nfs/NfsVolumeSnapshotProtector.java-26-27 (1)

26-27: 错误消息存在语法错误。

根据编码规范,代码中的英文应当正确无拼写错误。"seems not belong" 语法不正确,应改为 "does not seem to belong"。

📝 建议修复
-            completion.fail(inerr(ORG_ZSTACK_STORAGE_PRIMARY_NFS_10012, "the snapshot[name:%s, uuid:%s, path: %s] seems not belong to the volume[uuid:%s]",
+            completion.fail(inerr(ORG_ZSTACK_STORAGE_PRIMARY_NFS_10012, "the snapshot[name:%s, uuid:%s, path: %s] does not seem to belong to the volume[uuid:%s]",
network/src/main/java/org/zstack/network/service/HostRouteExtension.java-88-92 (1)

88-92: 错误消息存在语法问题。

根据编码规范,错误消息应使用正确的英文。当前消息 "L3Network [uuid: %s] provide type null" 语法不正确,建议修改为更清晰的表述。

📝 建议修复
-            reply.setError(operr(ORG_ZSTACK_NETWORK_SERVICE_10000, "L3Network [uuid: %s] provide type null", msg.getL3NetworkUuid()));
+            reply.setError(operr(ORG_ZSTACK_NETWORK_SERVICE_10000, "L3Network [uuid: %s] provider type is null", msg.getL3NetworkUuid()));
compute/src/main/java/org/zstack/compute/allocator/HostSortorChain.java-136-137 (1)

136-137: 错误消息中 e.getMessage() 重复使用。

格式字符串中有 3 个 %s 占位符,但 e.getMessage() 被使用了两次(开头和结尾),导致异常消息在输出中重复出现。建议移除末尾的 e.getMessage()

🐛 修复建议
                            trigger.fail(operr(
-                            ORG_ZSTACK_COMPUTE_ALLOCATOR_10034,         "[Host Allocation]: %s on host[uuid:%s]. try next one. %s", e.getMessage(), host.getUuid(), e.getMessage()));
+                            ORG_ZSTACK_COMPUTE_ALLOCATOR_10034, "[Host Allocation]: %s on host[uuid:%s]. try next one.", e.getMessage(), host.getUuid()));
plugin/kvm/src/main/java/org/zstack/kvm/KVMConsoleHypervisorBackend.java-65-67 (1)

65-67: 错误信息的英文表达不够规范。

根据编码规范,错误信息应使用正确的英文。"operation error, because:%s" 语法不够自然,建议改为更清晰的表达。

建议修改
-                    complete.fail(operr(ORG_ZSTACK_KVM_10160, "operation error, because:%s", rsp.getError()));
+                    complete.fail(operr(ORG_ZSTACK_KVM_10160, "failed to get VNC port: %s", rsp.getError()));
compute/src/main/java/org/zstack/compute/vm/TfVmNicFactory.java-44-46 (1)

44-46: AI 摘要与代码不一致 & 拼写错误

  1. AI 摘要不一致:AI 摘要中提到错误码为 ORG_ZSTACK_COMPUTE_VM_10089,但实际代码使用的是 ORG_ZSTACK_COMPUTE_VM_10091,请确认使用的错误码是正确的。

  2. 拼写建议:错误消息中的 "re-try" 应为 "retry"(一个单词),根据编码规范,代码中不应有拼写错误。

🔧 建议修复拼写错误
-            throw new FlowException(err(ORG_ZSTACK_COMPUTE_VM_10091, VmErrors.ALLOCATE_MAC_ERROR, "unable to find an available mac address after re-try 5 times, too many collisions"));
+            throw new FlowException(err(ORG_ZSTACK_COMPUTE_VM_10091, VmErrors.ALLOCATE_MAC_ERROR, "unable to find an available mac address after retrying 5 times, too many collisions"));
plugin/localstorage/src/main/java/org/zstack/storage/primary/local/LocalStorageSnapshotDeletionProtector.java-26-27 (1)

26-27: 错误消息存在语法错误。

根据编码规范,错误消息应使用正确的英文。当前 "seems not belong" 语法不正确,应改为 "does not seem to belong"

📝 建议修复
-            completion.fail(inerr(ORG_ZSTACK_STORAGE_PRIMARY_LOCAL_10019, "the snapshot[name:%s, uuid:%s, path: %s] seems not belong to the volume[uuid:%s]",
+            completion.fail(inerr(ORG_ZSTACK_STORAGE_PRIMARY_LOCAL_10019, "the snapshot[name:%s, uuid:%s, path: %s] does not seem to belong to the volume[uuid:%s]",
plugin/directory/src/main/java/org/zstack/directory/DirectoryManagerImpl.java-154-156 (1)

154-156: 错误消息存在语法问题。

根据编码规范要求使用正确的英文,"fail to create directory" 应为 "failed to create directory"。

🐛 建议的修复
-            completion.fail(operr(ORG_ZSTACK_DIRECTORY_10009, "fail to create directory, directories are up to four levels"));
+            completion.fail(operr(ORG_ZSTACK_DIRECTORY_10009, "failed to create directory, directories can have at most four levels"));
identity/src/main/java/org/zstack/identity/AccountQuotaUpdateChecker.java-32-35 (1)

32-35: 语法修正:"can not" 应为 "cannot"

根据编码规范,错误消息应使用正确的英文。"can not" 应合并为 "cannot"。

✏️ 建议的修改
         if (updatedValue < 0) {
-            return argerr(ORG_ZSTACK_IDENTITY_10016, "the quota[name:%s] of account[uuid:%s] can not be %d",
+            return argerr(ORG_ZSTACK_IDENTITY_10016, "the quota[name:%s] of account[uuid:%s] cannot be %d",
                     quota.getName(), quota.getIdentityUuid(), updatedValue);
         }
plugin/nfsPrimaryStorage/src/main/java/org/zstack/storage/primary/nfs/NfsPrimaryStorageKVMBackend.java-1253-1260 (1)

1253-1260: 错误消息中存在格式问题。

Line 1258-1260 的错误消息中包含了字面字符串 "installPath, pinv.getUuid(), rsp.getError()" 而不是实际格式化的变量值。这看起来像是复制粘贴错误或遗漏的格式化参数。

🔧 建议修复
-                    completion.fail(operr(ORG_ZSTACK_STORAGE_PRIMARY_NFS_10036, "failed to delete bits[%s] on nfs primary storage[uuid:%s], %s, will clean up " +
-                                    "installPath, pinv.getUuid(), rsp.getError()",
-                            installPath, pinv.getUuid(), rsp.getError()));
+                    completion.fail(operr(ORG_ZSTACK_STORAGE_PRIMARY_NFS_10036, "failed to delete bits[%s] on nfs primary storage[uuid:%s], %s, will clean up",
+                            installPath, pinv.getUuid(), rsp.getError()));
core/src/main/java/org/zstack/core/errorcode/ElaborationManagerImpl.java-70-79 (1)

70-79: 错误消息存在英文语法问题。

根据编码规范,错误消息应使用正确的英文。当前消息存在语法错误:

  • Line 72: "non file or jsoncontent input" → 应为 "no file or json content input""neither file nor json content provided"
  • Line 77: "file or jsoncontent cannot both nonempty" → 应为 "file and json content cannot both be non-empty"
建议修复
     private void preCheckElaborationContent(String filename, String jsonContent, ReturnValueCompletion<List<ElaborationCheckResult>> completion) {
         if (filename == null && jsonContent == null) {
-            completion.fail(argerr(ORG_ZSTACK_CORE_ERRORCODE_10000, "non file or jsoncontent input"));
+            completion.fail(argerr(ORG_ZSTACK_CORE_ERRORCODE_10000, "neither file nor json content provided"));
             return;
         }

         if (filename != null && jsonContent != null) {
-            completion.fail(argerr(ORG_ZSTACK_CORE_ERRORCODE_10001, "file or jsoncontent cannot both nonempty"));
+            completion.fail(argerr(ORG_ZSTACK_CORE_ERRORCODE_10001, "file and json content cannot both be non-empty"));
             return;
         }
core/src/main/java/org/zstack/core/errorcode/ElaborationManagerImpl.java-108-116 (1)

108-116: 错误消息存在英文语法问题。

Line 113 的错误消息语法不正确:"%s is not existed or is empty folder"

建议修复
-                                trigger.fail(argerr(ORG_ZSTACK_CORE_ERRORCODE_10003, "%s is not existed or is empty folder", filename));
+                                trigger.fail(argerr(ORG_ZSTACK_CORE_ERRORCODE_10003, "%s does not exist or is an empty folder", filename));
image/src/main/java/org/zstack/image/ImageApiInterceptor.java-180-182 (1)

180-182: 错误消息中包含中文字符。

错误消息中的 'https://', 使用了中文逗号 ,应改为英文逗号 ,。根据编码规范,代码中不应包含中文字符。

🔧 建议修复
-            throw new ApiMessageInterceptionException(argerr(ORG_ZSTACK_IMAGE_10011, "url must starts with 'file:///', 'http://', 'https://', 'ftp://', 'sftp://' or '/'"));
+            throw new ApiMessageInterceptionException(argerr(ORG_ZSTACK_IMAGE_10011, "url must start with 'file:///', 'http://', 'https://', 'ftp://', 'sftp://' or '/'"));

注:同时修正了 "starts" 为 "start" 以符合语法。

image/src/main/java/org/zstack/image/ImageApiInterceptor.java-191-193 (1)

191-193: 格式化字符串存在未使用的参数。

argerr 调用中传递了 path 参数,但格式化字符串 "absolute path must be used" 中没有对应的占位符 %s。这是一个潜在的 bug,建议在错误消息中包含无效路径以便于调试。

🔧 建议修复
-            throw new ApiMessageInterceptionException(argerr(ORG_ZSTACK_IMAGE_10012, "absolute path must be used", path));
+            throw new ApiMessageInterceptionException(argerr(ORG_ZSTACK_IMAGE_10012, "absolute path must be used, got: %s", path));
plugin/ldap/src/main/java/org/zstack/ldap/LdapApiInterceptor.java-152-161 (1)

152-161: Line 157 存在语法错误。

"communication false" 应为 "communication failed""communication failure"。根据编码规范,错误信息应使用正确的英文。

📝 修复英文语法
         } catch (CommunicationException e) {
             logger.debug("Cannot connect to LDAP/AD server, communication false, please checkout IP, port and Base DN", e);
-            return operr(ORG_ZSTACK_LDAP_10013, "Cannot connect to LDAP/AD server, communication false, please checkout IP, port and Base DN");
+            return operr(ORG_ZSTACK_LDAP_10013, "Cannot connect to LDAP/AD server, communication failed, please check IP, port and Base DN");
         } catch (Exception e) {

注意:同时建议将 checkout 改为 check,因为 checkout 通常用于版本控制或结账场景。

plugin/flatNetworkProvider/src/main/java/org/zstack/network/service/flat/FlatDhcpBackend.java-2494-2498 (1)

2494-2498: 错误信息使用了错误的参数

错误信息提示 "dhcp v6 server ip",但格式化参数使用的是 msg.getDhcpServerIp() 而非 msg.getDhcpv6ServerIp()。这会导致用户看到的错误信息显示错误的 IP 地址。

🐛 修复建议
         if (msg.getDhcpv6ServerIp() != null) {
             if (Q.New(UsedIpVO.class).eq(UsedIpVO_.ip, msg.getDhcpv6ServerIp())
                     .eq(UsedIpVO_.l3NetworkUuid, msg.getL3NetworkUuid()).isExists()) {
                 throw new ApiMessageInterceptionException(argerr(ORG_ZSTACK_NETWORK_SERVICE_FLAT_10051, "could set dhcp v6 server ip, because ip[%s] is used",
-                        msg.getDhcpServerIp()));
+                        msg.getDhcpv6ServerIp()));
             }
         }
plugin/acl/src/main/java/org/zstack/acl/AccessControlListApiInterceptor.java-190-192 (1)

190-192: 错误信息中使用了错误的变量。

第 191 行的错误消息提示 URL 无效,但传入的格式化参数却是 msg.getDomain(),应该使用 msg.getUrl()

🐛 修复建议
         } else {
             if (!AccessControlListUtils.isValidateUrl(msg.getUrl())) {
-                throw new ApiMessageInterceptionException(argerr(ORG_ZSTACK_ACL_10014, "url[%s] is not validate url", msg.getDomain()));
+                throw new ApiMessageInterceptionException(argerr(ORG_ZSTACK_ACL_10014, "url[%s] is not a valid url", msg.getUrl()));
             }
             msg.setMatchMethod("Url");
         }
compute/src/main/java/org/zstack/compute/allocator/ImageBackupStorageAllocatorFlow.java-36-36 (1)

36-36: 修复第145行的调用方式以保持代码一致性。

错误码常量已正确定义,但存在调用风格不一致的问题。第112行和第123行使用了静态导入形式(operr(...)err(...)),而第145行使用了完全限定形式(Platform.operr(...))。建议统一改为静态导入形式以保持代码风格一致。

plugin/loadBalancer/src/main/java/org/zstack/network/service/lb/LoadBalancerBase.java-854-856 (1)

854-856: 拼写错误: "cloud" 应为 "could"。

根据编码规范,错误信息应使用正确且无拼写错误的英文。

🐛 修复建议
-                       reply.setError(operr(ORG_ZSTACK_NETWORK_SERVICE_LB_10020, "cloud not delete vip[%s]", vipVO.getUuid()));
+                       reply.setError(operr(ORG_ZSTACK_NETWORK_SERVICE_LB_10020, "could not delete vip[%s]", vipVO.getUuid()));
plugin/loadBalancer/src/main/java/org/zstack/network/service/lb/LoadBalancerBase.java-776-779 (1)

776-779: Bug: IPv6 VIP 错误信息使用了错误的 getter 方法。

错误信息提到 "ipv6 vip",但代码使用 self.getVipUuid() 获取的是 IPv4 VIP UUID。应使用 self.getIpv6VipUuid() 以正确显示已存在的 IPv6 VIP UUID。

🐛 修复建议
                 if (!StringUtils.isEmpty(self.getIpv6VipUuid())) {
-                    throw new OperationFailureException(operr(ORG_ZSTACK_NETWORK_SERVICE_LB_10019, "fail to attach ipv6 vip to lb , because lb[%s] has ipv6 vip[%s]", self.getUuid(), self.getVipUuid()));
+                    throw new OperationFailureException(operr(ORG_ZSTACK_NETWORK_SERVICE_LB_10019, "fail to attach ipv6 vip to lb, because lb[%s] has ipv6 vip[%s]", self.getUuid(), self.getIpv6VipUuid()));
                 }
identity/src/main/java/org/zstack/identity/QuotaUtil.java-35-38 (1)

35-38: 格式问题:静态导入语句与注释合并在同一行。

静态导入语句末尾缺少换行符,导致原有的文档注释与导入语句合并在一起。这会影响代码可读性。

🔧 建议修复
 import static org.zstack.core.Platform.err;
-import static org.zstack.utils.clouderrorcode.CloudOperationsErrorCode.*;/**
- * Created by miao on 16-10-9.
- */
-
+import static org.zstack.utils.clouderrorcode.CloudOperationsErrorCode.*;
+
+/**
+ * Created by miao on 16-10-9.
+ */
 @Configurable(preConstruction = true, autowire = Autowire.BY_TYPE)
plugin/ceph/src/main/java/org/zstack/storage/ceph/CephApiInterceptor.java-81-84 (1)

81-84: 输入参数未做 trim 处理,且错误消息格式冗余。

  1. msg.getPoolName() 来自 API 消息,用户可能在浏览器复制粘贴时带入空格或换行符,应先 trim 后再校验。
  2. 错误消息 "poolName[poolName:%s]"poolName 重复,建议简化为 "poolName[%s]"

基于 learnings,Interceptor 中来自 Message 的外部字符串参数都应进行 trim 处理。

建议修复
 private void validate(APIAddCephPrimaryStoragePoolMsg msg) {
-    if (!CharacterUtils.checkCharacter(msg.getPoolName())){
-        throw new ApiMessageInterceptionException(argerr(ORG_ZSTACK_STORAGE_CEPH_10006, "operation failure, because the poolName[poolName:%s] can not include unprintable ascii characters.", msg.getPoolName()));
+    String poolName = msg.getPoolName() != null ? msg.getPoolName().trim() : null;
+    if (poolName != null && !CharacterUtils.checkCharacter(poolName)){
+        throw new ApiMessageInterceptionException(argerr(ORG_ZSTACK_STORAGE_CEPH_10006, "operation failure, because the poolName[%s] can not include unprintable ascii characters.", poolName));
     }
plugin/ceph/src/main/java/org/zstack/storage/ceph/CephApiInterceptor.java-245-250 (1)

245-250: poolName 参数应做 trim 处理。

msg.getPoolName() 应在校验前 trim,与其他 validate 方法保持一致。

image/src/main/java/org/zstack/image/UploadImageTracker.java-213-217 (1)

213-217: 修正错误消息中的英文语法。

当前错误消息:"uploading has been inactive more than %d sec" 应改为 "uploading has been inactive for more than %d sec"(添加介词 for 以符合英文语法规范)。

建议修改
-                    markFailure(err(ORG_ZSTACK_IMAGE_10020, ImageErrors.UPLOAD_IMAGE_INTERRUPTED, reply.getError(),
-                            "uploading has been inactive more than %d sec", maxIdleSecond));
+                    markFailure(err(ORG_ZSTACK_IMAGE_10020, ImageErrors.UPLOAD_IMAGE_INTERRUPTED, reply.getError(),
+                            "uploading has been inactive for more than %d sec", maxIdleSecond));
plugin/localstorage/src/main/java/org/zstack/storage/primary/local/LocalStorageKvmBackend.java-3819-3822 (1)

3819-3822: 潜在的空指针异常风险

与上方 checkHostAttachedPSMountPath 方法相同,errorCode.getCause() 可能返回 null

建议添加空值保护:

🐛 建议修复
-completion.fail(operr(ORG_ZSTACK_STORAGE_PRIMARY_LOCAL_10082, "cannot create flag file [%s] on host [%s], because: %s", makeInitializedFilePath(), hostUuid, errorCode.getCause().getDetails()));
+String causeDetails = errorCode.getCause() != null ? errorCode.getCause().getDetails() : errorCode.getDetails();
+completion.fail(operr(ORG_ZSTACK_STORAGE_PRIMARY_LOCAL_10082, "cannot create flag file [%s] on host [%s], because: %s", makeInitializedFilePath(), hostUuid, causeDetails));
plugin/localstorage/src/main/java/org/zstack/storage/primary/local/LocalStorageKvmBackend.java-3798-3801 (1)

3798-3801: 潜在的空指针异常风险

errorCode.getCause() 可能返回 null,此时调用 .getDetails() 会抛出 NullPointerException

建议添加空值保护:

🐛 建议修复
-completion.fail(operr(ORG_ZSTACK_STORAGE_PRIMARY_LOCAL_10081, "cannot find flag file [%s] on host [%s], because: %s", makeInitializedFilePath(), hostUuid, errorCode.getCause().getDetails()));
+String causeDetails = errorCode.getCause() != null ? errorCode.getCause().getDetails() : errorCode.getDetails();
+completion.fail(operr(ORG_ZSTACK_STORAGE_PRIMARY_LOCAL_10081, "cannot find flag file [%s] on host [%s], because: %s", makeInitializedFilePath(), hostUuid, causeDetails));
plugin/ceph/src/main/java/org/zstack/storage/ceph/backup/CephBackupStorageBase.java-1959-1962 (1)

1959-1962: 发现拼写错误。

错误码 ORG_ZSTACK_STORAGE_CEPH_BACKUP_10010 添加正确,但错误信息中存在拼写错误:cep cluster 应为 ceph cluster

🐛 建议修复
-                                        errors.add(operr(ORG_ZSTACK_STORAGE_CEPH_BACKUP_10010, "the mon[ip:%s] returns a fsid[%s] different from the current fsid[%s] of the cep cluster," +
+                                        errors.add(operr(ORG_ZSTACK_STORAGE_CEPH_BACKUP_10010, "the mon[ip:%s] returns a fsid[%s] different from the current fsid[%s] of the ceph cluster," +
compute/src/main/java/org/zstack/compute/vm/VmInstanceApiInterceptor.java-215-218 (1)

215-218: 错误信息里的换行符写成了 /n(应为 \n

"please detach manually/n" + networkServices.toString() 会把 /n 原样输出,影响可读性。

建议修复
-            String error = "vm nic [%s] attached network services, please detach manually/n" + networkServices.toString();
+            String error = "vm nic [%s] attached network services, please detach manually\n" + networkServices;
             throw new ApiMessageInterceptionException(operr(ORG_ZSTACK_COMPUTE_VM_10095, error, msg.getVmNicUuid()));
compute/src/main/java/org/zstack/compute/vm/VmInstanceApiInterceptor.java-1558-1561 (1)

1558-1561: Deprecated API 的报错文案建议更规范

"can not call this api because it's Deprecated":大小写/语法不够统一,建议用 “cannot … because it is deprecated”。

建议修复
-        throw new ApiMessageInterceptionException(argerr(ORG_ZSTACK_COMPUTE_VM_10219, "can not call this api because it's Deprecated"));
+        throw new ApiMessageInterceptionException(argerr(ORG_ZSTACK_COMPUTE_VM_10219, "cannot call this API because it is deprecated"));
plugin/ceph/src/main/java/org/zstack/storage/ceph/primary/CephPrimaryStorageBase.java-4333-4337 (1)

4333-4337: 修正用户可见英文错误信息的拼写/语法(避免影响检索、告警聚合与用户体验)。
至少两处明显问题:cep cluster 拼写错误、逗号后缺空格;另外 unable to connect mons 信息略短但可接受。

建议修改
- errors.add(operr(ORG_ZSTACK_STORAGE_CEPH_PRIMARY_10017, "the mon[ip:%s] returns a fsid[%s] different from the current fsid[%s] of the cep cluster," +
-         "are you adding a mon not belonging to current cluster mistakenly?", base.getSelf().getHostname(), fsid, getSelf().getFsid())
+ errors.add(operr(ORG_ZSTACK_STORAGE_CEPH_PRIMARY_10017, "the mon[ip:%s] returns a fsid[%s] different from the current fsid[%s] of the ceph cluster, " +
+         "are you adding a mon not belonging to the current cluster mistakenly?", base.getSelf().getHostname(), fsid, getSelf().getFsid())
 );

Also applies to: 4370-4377, 4389-4394

plugin/ceph/src/main/java/org/zstack/storage/ceph/primary/CephPrimaryStorageBase.java-5272-5276 (1)

5272-5276: 建议统一错误信息里的英文单词与空格:root volume / image cache / backup storage / primary storage。
这些信息通常会被用户/支持同学直接复制检索,建议保持规范拼写。

建议修改
- throw new OperationFailureException(operr(ORG_ZSTACK_STORAGE_CEPH_PRIMARY_10029, "cannot reinit rootvolume [%s] because image [%s] has been deleted and imagecache cannot be found",
+ throw new OperationFailureException(operr(ORG_ZSTACK_STORAGE_CEPH_PRIMARY_10029, "cannot re-init root volume[%s] because image[%s] has been deleted and image cache cannot be found",
         volume.getUuid(), volume.getRootImageUuid()));
- })).orElseThrow(() -> new OperationFailureException(operr(ORG_ZSTACK_STORAGE_CEPH_PRIMARY_10030, "cannot find backupstorage to download image [%s] " +
-         "to primarystorage [%s] due to lack of Ready and accessible image", volume.getRootImageUuid(), getSelf().getUuid())));
+ })).orElseThrow(() -> new OperationFailureException(operr(ORG_ZSTACK_STORAGE_CEPH_PRIMARY_10030, "cannot find backup storage to download image[%s] " +
+         "to primary storage[%s] due to lack of a Ready and accessible image", volume.getRootImageUuid(), getSelf().getUuid())));

Also applies to: 5301-5303

plugin/ceph/src/main/java/org/zstack/storage/ceph/primary/CephPrimaryStorageBase.java-5058-5061 (1)

5058-5061: “deprecated”拼写错误:depreciated 应为 deprecated
该字符串用户可见,建议顺手修正并润色语法。

建议修改
- reply.setError(operr(ORG_ZSTACK_STORAGE_CEPH_PRIMARY_10028, "backing up snapshots to backup storage is a depreciated feature, which will be removed in future version"));
+ reply.setError(operr(ORG_ZSTACK_STORAGE_CEPH_PRIMARY_10028, "backing up snapshots to backup storage is a deprecated feature and will be removed in a future version"));
plugin/kvm/src/main/java/org/zstack/kvm/KVMHost.java-4151-4153 (1)

4151-4153: **修正错误文案拼写(faild → failed)。**这类字符串会进入日志/返回值,建议在本 PR 顺手修掉。

建议修改
-                    reply.setError(operr(ORG_ZSTACK_KVM_10079, "Host[%s] update spice channel config faild, because %s", msg.getHostUuid(), ret.getError()));
+                    reply.setError(operr(ORG_ZSTACK_KVM_10079, "Host[%s] update spice channel config failed, because %s", msg.getHostUuid(), ret.getError()));

Comment on lines 113 to 117
if (type == null) {
errs.add(Platform.operr("there is no available nicType on L3 network [%s]", nw.getUuid()));
errs.add(Platform.operr(ORG_ZSTACK_COMPUTE_VM_10068, "there is no available nicType on L3 network [%s]", nw.getUuid()));
wcomp.allDone();
}
VmInstanceNicFactory vnicFactory = vmMgr.getVmInstanceNicFactory(type);
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

控制流问题:wcomp.allDone() 后缺少 return 语句。

type == null 时,代码添加错误并调用 wcomp.allDone(),但没有 return,导致后续第 117 行 vmMgr.getVmInstanceNicFactory(type) 会以 null 参数执行,可能导致 NullPointerException。

错误码 ORG_ZSTACK_COMPUTE_VM_10068 的添加是正确的,但建议同时修复此控制流问题。

🐛 建议修复
         if (type == null) {
             errs.add(Platform.operr(ORG_ZSTACK_COMPUTE_VM_10068, "there is no available nicType on L3 network [%s]", nw.getUuid()));
             wcomp.allDone();
+            return;
         }
🤖 Prompt for AI Agents
In @compute/src/main/java/org/zstack/compute/vm/VmAllocateNicFlow.java around
lines 113 - 117, In VmAllocateNicFlow, when type == null the code adds an error
via errs.add(...) and calls wcomp.allDone() but does not return, so
vmMgr.getVmInstanceNicFactory(type) is invoked with a null type; fix by adding
an immediate return after wcomp.allDone() (i.e., ensure the control flow exits
the method/closure right after wcomp.allDone() to prevent calling
vmMgr.getVmInstanceNicFactory(type) with null).

trigger.next();
} else {
trigger.fail(operr("failed to configure consoleProxyOverriddenIp[code:%d] or consoleProxyPort[code:%d]"));
trigger.fail(operr(ORG_ZSTACK_CONSOLE_10003, "failed to configure consoleProxyOverriddenIp[code:%d] or consoleProxyPort[code:%d]"));
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

格式化参数缺失导致运行时错误

错误消息包含两个 %d 占位符,但未传递对应的格式化参数 rstportRst。这将导致运行时格式化异常或输出不正确的错误信息。

🐛 修复建议
-                            trigger.fail(operr(ORG_ZSTACK_CONSOLE_10003, "failed to configure consoleProxyOverriddenIp[code:%d] or consoleProxyPort[code:%d]"));
+                            trigger.fail(operr(ORG_ZSTACK_CONSOLE_10003, "failed to configure consoleProxyOverriddenIp[code:%d] or consoleProxyPort[code:%d]", rst, portRst));
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
trigger.fail(operr(ORG_ZSTACK_CONSOLE_10003, "failed to configure consoleProxyOverriddenIp[code:%d] or consoleProxyPort[code:%d]"));
trigger.fail(operr(ORG_ZSTACK_CONSOLE_10003, "failed to configure consoleProxyOverriddenIp[code:%d] or consoleProxyPort[code:%d]", rst, portRst));
🤖 Prompt for AI Agents
In
@console/src/main/java/org/zstack/console/ManagementServerConsoleProxyBackend.java
at line 571, The error message invocation to
trigger.fail(operr(ORG_ZSTACK_CONSOLE_10003, "failed to configure
consoleProxyOverriddenIp[code:%d] or consoleProxyPort[code:%d]")) is missing the
formatting arguments; update the call to pass the actual status variables (rst
and portRst) into operr so the two %d placeholders are filled (e.g.,
operr(ORG_ZSTACK_CONSOLE_10003, "failed to configure
consoleProxyOverriddenIp[code:%d] or consoleProxyPort[code:%d]", rst, portRst));
locate this in ManagementServerConsoleProxyBackend where trigger.fail(...) is
invoked and ensure rst and portRst are in scope and used as the format
parameters.

List<String> deniedApis = permissionResult.entrySet().stream().filter(entry -> !entry.getValue()).map(Map.Entry::getKey).collect(Collectors.toList());
if (!deniedApis.isEmpty()) {
throw new OperationFailureException(operr("the operations[%s] is denied", deniedApis));
throw new OperationFailureException(operr(ORG_ZSTACK_IDENTITY_10005, "the operations[%s] is denied", deniedApis));
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# 验证语法问题、错误码定义和异常处理方式

# 1. 查看文件上下文(60-80行)
echo "=== 查看 DefaultAuthorizationBackend.java 的上下文 ==="
sed -n '60,80p' identity/src/main/java/org/zstack/identity/DefaultAuthorizationBackend.java

echo -e "\n=== 搜索错误码定义 ==="
rg -n "ORG_ZSTACK_IDENTITY_10005" --type java -C 2

echo -e "\n=== 查看该文件导入的异常类 ==="
head -40 identity/src/main/java/org/zstack/identity/DefaultAuthorizationBackend.java | grep -E "import.*Exception|import.*Interception"

echo -e "\n=== 检查方法签名和类定义 ==="
sed -n '1,100p' identity/src/main/java/org/zstack/identity/DefaultAuthorizationBackend.java | grep -E "class |extends |implements |public.*void"

Repository: MatheMatrix/zstack

Length of output: 180


🏁 Script executed:

# 需要验证的关键信息:

1. 查看文件上下文(行60-80)
   - 该方法的完整签名
   - 该方法的类型(是否属于API拦截器/验证逻辑)
   - deniedApis 参数的完整类型定义

2. 查找错误码 ORG_ZSTACK_IDENTITY_10005 的定义
   rg -n "ORG_ZSTACK_IDENTITY_10005" --type java -C 2

3. 查看文件导入
   head -50 identity/src/main/java/org/zstack/identity/DefaultAuthorizationBackend.java | grep "import"

4. 查看方法所在的类上下文
   sed -n '1,100p' identity/src/main/java/org/zstack/identity/DefaultAuthorizationBackend.java

Repository: MatheMatrix/zstack

Length of output: 180


🔍 Additional research query:

`Search DevinDeepWiki for MatheMatrix/zstack repository information, specifically looking for:

  1. DefaultAuthorizationBackend.java file content around line 67
  2. Error code ORG_ZSTACK_IDENTITY_10005 definition
  3. ApiMessageInterceptionException vs OperationFailureException usage patterns in identity module`

💡 Result:

Excellent! I've gathered comprehensive information about your query from the MatheMatrix/zstack repository. Here's a summary of the findings:

1. DefaultAuthorizationBackend.java around Line 67

The validatePermission method in DefaultAuthorizationBackend.java contains the core authorization logic:

  • Permission Validation: Uses RBACAPIRequestChecker.evalAPIPermission() to evaluate API permissions for a list of API classes and a SessionInventory
  • Denied APIs Detection: Filters the permission map to identify APIs where permission is denied (boolean value is false)
  • Error Handling: If any APIs are denied, an OperationFailureException is thrown with the names of denied operations included in the error message
  • Integration Point: Called by AuthorizationManager in its beforeQueryExtensionPoint method, occurring before query execution
  • Architecture: Configured as a Spring bean in AccountManager.xml and implements the AuthorizationBackend interface

2. Error Code ORG_ZSTACK_IDENTITY_10005

The error code ORG_ZSTACK_IDENTITY_10005 is defined as NEED_CHANGE_PASSWORD within the IdentityErrors enum:

  • Definition: Located in the IdentityErrors enum with integer ID 1005
  • Meaning: Indicates that a password change is required
  • Formatting: The toString() method formats it as ID.1005 internally; the external representation uses the prefix ORG_ZSTACK_IDENTITY_
  • Localization: Localized messages are provided in messages_en_US.properties and messages_zh_CN.properties files

3. ApiMessageInterceptionException vs OperationFailureException Usage Patterns

The key distinction is the processing phase where each is thrown:

ApiMessageInterceptionException (Early-stage validation):

  • Thrown during API message interception phase (pre-processing)
  • Used for validation failures, malformed requests, and early authorization checks
  • Examples: duplicate resource names, null sessions, expired sessions, permission denials from policies, resource ownership violations
  • Acts as a gatekeeper before core API logic executes

OperationFailureException (Execution-stage failures):

  • Thrown during actual operation execution after initial message validation
  • Used for business logic violations, permission denials from RBAC checks, resource constraints
  • Examples: password mismatches, permission denials during operation execution, admin-only policy violations, expired sessions during operation

Key Pattern: ApiMessageInterceptionException handles validation at the request level, while OperationFailureException handles failures during business logic execution.


🔍 Additional research query:

Search DevinDeepWiki for ZStack identity module authorization and exception handling patterns, particularly regarding when to use ApiMessageInterceptionException versus OperationFailureException

💡 Result:

Summary

I've successfully gathered comprehensive information about ZStack's identity module authorization and exception handling patterns. Based on the DevinDeepWiki documentation, here are the key findings:

Exception Handling Patterns in ZStack Identity Module

ApiMessageInterceptionException is specifically used within API message interceptors to indicate that an API message should not proceed due to validation failures or permission issues, and it's often used to prevent an API call from reaching its intended handler if it fails preliminary checks.

OperationFailureException, on the other hand, is a more general exception used to indicate that an operation has failed due to business logic errors or other issues during its execution, and it's typically thrown when an API message has passed initial interception checks and is being processed by the core logic.

When to Use Each Exception

ApiMessageInterceptionException Usage:
This exception is thrown during session validation (when a session is invalid, expired, or UUID is null), resource ownership checks (when a user attempts to operate on a resource they don't own), policy enforcement (when a policy explicitly denies an action or an API is marked as "admin only" and a non-admin user attempts to call it), and message parameter validation (such as trying to create an account with a duplicate name or attempting to delete the built-in admin account).

OperationFailureException Usage:
This exception is used when checking API permissions (if a non-admin account tries to check permissions for a user not belonging to its account), password updates (if an APIUpdateAccountMsg is received with an incorrect old password), and quota updates (when the specified quota cannot be found or if a QuotaUpdateChecker determines that the update is invalid).

Authorization Flow in Identity Module

Authorization in the identity module is handled through API message interception where all API messages are intercepted, the session is evaluated, a backend is selected for authorization, and the selected AuthorizationBackend's authorize method is called to perform the actual permission check.,


异常类型和错误处理方式不符合规范,另有文法错误。

  1. 异常类型错误:该方法在API消息拦截阶段执行权限验证,应该抛出 ApiMessageInterceptionException 而非 OperationFailureException。根据ZStack设计规范,权限拒绝检查属于早期验证阶段,需使用 ApiMessageInterceptionException 作为消息拦截的网关。

  2. 错误消息文法错误:"the operations[%s] is denied" 中 "operations" 是复数形式,动词应使用 "are"。

  3. 错误码不匹配ORG_ZSTACK_IDENTITY_10005 定义为 NEED_CHANGE_PASSWORD,不适合权限拒绝场景。需确认使用正确的权限拒绝相关错误码。

📝 建议的修改
-            throw new OperationFailureException(operr(ORG_ZSTACK_IDENTITY_10005, "the operations[%s] is denied", deniedApis));
+            throw new ApiMessageInterceptionException(operr(PERMISSION_CODE, "the operations[%s] are denied", deniedApis));

(注:需确认 PERMISSION_CODE 为权限拒绝的正确错误码)

🤖 Prompt for AI Agents
In @identity/src/main/java/org/zstack/identity/DefaultAuthorizationBackend.java
at line 67, In DefaultAuthorizationBackend replace the thrown
OperationFailureException with an ApiMessageInterceptionException at the API
message interception permission check, correct the error text to "the
operations[%s] are denied", and swap the erroneous ORG_ZSTACK_IDENTITY_10005
error code for the proper permission-denial error code (e.g., PERMISSION_CODE or
the project’s designated permission denial constant) so the exception and
message match the interception/permission semantics.

Comment on lines 781 to 795
private void validate(APIAddHostRouteToL3NetworkMsg msg) {
if (!NetworkUtils.isCidr(msg.getPrefix())) {
throw new ApiMessageInterceptionException(argerr("prefix [%s] is not a IPv4 network cidr", msg.getL3NetworkUuid()));
throw new ApiMessageInterceptionException(argerr(ORG_ZSTACK_NETWORK_L3_10069, "prefix [%s] is not a IPv4 network cidr", msg.getL3NetworkUuid()));
}

if (!NetworkUtils.isIpv4Address(msg.getNexthop())) {
throw new ApiMessageInterceptionException(argerr("nexthop[%s] is not a IPv4 address", msg.getNexthop()));
throw new ApiMessageInterceptionException(argerr(ORG_ZSTACK_NETWORK_L3_10070, "nexthop[%s] is not a IPv4 address", msg.getNexthop()));
}

SimpleQuery<L3NetworkHostRouteVO> q = dbf.createQuery(L3NetworkHostRouteVO.class);
q.add(L3NetworkHostRouteVO_.l3NetworkUuid, Op.EQ, msg.getL3NetworkUuid());
q.add(L3NetworkHostRouteVO_.prefix, Op.EQ, msg.getPrefix());
if (q.isExists()) {
throw new ApiMessageInterceptionException(operr("there has been a hostroute for prefix[%s] on L3 network[uuid:%s]", msg.getPrefix(), msg.getL3NetworkUuid()));
throw new ApiMessageInterceptionException(operr(ORG_ZSTACK_NETWORK_L3_10071, "there has been a hostroute for prefix[%s] on L3 network[uuid:%s]", msg.getPrefix(), msg.getL3NetworkUuid()));
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

错误消息中使用了错误的变量。

Line 783 验证的是 msg.getPrefix(),但错误消息中使用了 msg.getL3NetworkUuid()。用户需要知道哪个前缀值无效。

🐛 建议修复
     private void validate(APIAddHostRouteToL3NetworkMsg msg) {
         if (!NetworkUtils.isCidr(msg.getPrefix())) {
-            throw new ApiMessageInterceptionException(argerr(ORG_ZSTACK_NETWORK_L3_10069, "prefix [%s] is not a IPv4 network cidr", msg.getL3NetworkUuid()));
+            throw new ApiMessageInterceptionException(argerr(ORG_ZSTACK_NETWORK_L3_10069, "prefix [%s] is not a IPv4 network cidr", msg.getPrefix()));
         }
🤖 Prompt for AI Agents
In @network/src/main/java/org/zstack/network/l3/L3NetworkApiInterceptor.java
around lines 781 - 795, In validate(APIAddHostRouteToL3NetworkMsg msg) in
L3NetworkApiInterceptor the first argerr uses the wrong variable: it validates
msg.getPrefix() but logs msg.getL3NetworkUuid(); change the error message to
include msg.getPrefix() (i.e., replace msg.getL3NetworkUuid() with
msg.getPrefix()) so the exception reports the invalid CIDR value being checked.

Comment on lines 798 to 809
private void validate(APIRemoveHostRouteFromL3NetworkMsg msg) {
if (!NetworkUtils.isCidr(msg.getPrefix())) {
throw new ApiMessageInterceptionException(argerr("prefix [%s] is not a IPv4 network cidr", msg.getL3NetworkUuid()));
throw new ApiMessageInterceptionException(argerr(ORG_ZSTACK_NETWORK_L3_10072, "prefix [%s] is not a IPv4 network cidr", msg.getL3NetworkUuid()));
}

SimpleQuery<L3NetworkHostRouteVO> q = dbf.createQuery(L3NetworkHostRouteVO.class);
q.add(L3NetworkHostRouteVO_.l3NetworkUuid, Op.EQ, msg.getL3NetworkUuid());
q.add(L3NetworkHostRouteVO_.prefix, Op.EQ, msg.getPrefix());
if (!q.isExists()) {
throw new ApiMessageInterceptionException(operr("there is no hostroute for prefix[%s] on L3 network[uuid:%s]", msg.getPrefix(), msg.getL3NetworkUuid()));
throw new ApiMessageInterceptionException(operr(ORG_ZSTACK_NETWORK_L3_10073, "there is no hostroute for prefix[%s] on L3 network[uuid:%s]", msg.getPrefix(), msg.getL3NetworkUuid()));
}
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

错误消息中使用了错误的变量。

与上述 APIAddHostRouteToL3NetworkMsg 相同的问题,Line 800 验证的是 msg.getPrefix(),但错误消息中却使用了 msg.getL3NetworkUuid()

🐛 建议修复
     private void validate(APIRemoveHostRouteFromL3NetworkMsg msg) {
         if (!NetworkUtils.isCidr(msg.getPrefix())) {
-            throw new ApiMessageInterceptionException(argerr(ORG_ZSTACK_NETWORK_L3_10072, "prefix [%s] is not a IPv4 network cidr", msg.getL3NetworkUuid()));
+            throw new ApiMessageInterceptionException(argerr(ORG_ZSTACK_NETWORK_L3_10072, "prefix [%s] is not a IPv4 network cidr", msg.getPrefix()));
         }
🤖 Prompt for AI Agents
In @network/src/main/java/org/zstack/network/l3/L3NetworkApiInterceptor.java
around lines 798 - 809, In validate(APIRemoveHostRouteFromL3NetworkMsg msg) the
first ApiMessageInterceptionException formats the error with the wrong variable
(uses msg.getL3NetworkUuid()); change the formatted argument to msg.getPrefix()
so the argerr call reads "prefix [%s] is not a IPv4 network cidr" with
msg.getPrefix() to correctly report the invalid prefix.

Resolves: ZSTAC-66343

Change-Id: I6e6b6f76676c7671786e79696f70746268717864
@MatheMatrix MatheMatrix force-pushed the sync/lin.ma/5.5.6_global_error_code@@2 branch from 8fba7f4 to 10147e4 Compare January 12, 2026 04:50
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 8

Note

Due to the large number of review comments, Critical, Major severity comments were prioritized as inline comments.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (12)
compute/src/main/java/org/zstack/compute/vm/VmInstanceExtensionPointEmitter.java (1)

142-155: preStopVm 方法未更新为使用全局错误码,与其他 pre 方法不一致。*

此方法仍使用 errf.instantiateErrorCode(SysErrors.OPERATION_ERROR, ...) 模式,而其他类似方法(preStartNewCreatedVmpreRebootVmpreDestroyVmpreStartVm)均已更新为使用 operr(ORG_ZSTACK_COMPUTE_VM_xxxxx, ...) 模式。建议补充对应的全局错误码以保持一致性。

🔧 建议修复
     public ErrorCode preStopVm(VmInstanceInventory inv) {
         for (VmInstanceStopExtensionPoint ext : stopVmExtensions) {
             try {
                 String err = ext.preStopVm(inv);
                 if (err != null) {
-                    return errf.instantiateErrorCode(SysErrors.OPERATION_ERROR, String.format("VmInstanceStopVmExtensionPoint[%s] refuses to stop vm[uuid:%s] because %s",
-                            ext.getClass().getName(), inv.getUuid(), err));
+                    return operr(ORG_ZSTACK_COMPUTE_VM_XXXXX, "VmInstanceStopVmExtensionPoint[%s] refuses to stop vm[uuid:%s] because %s",
+                            ext.getClass().getName(), inv.getUuid(), err);
                 }
             } catch (Exception e) {
                 logger.warn(String.format("Unhandled exception while calling %s", ext.getClass().getName()), e);
             }
         }
         return null;
     }

注意:请根据 ZSTAC-66343 的错误码映射规范确定正确的 ORG_ZSTACK_COMPUTE_VM_XXXXX 常量值。

console/src/main/java/org/zstack/console/AbstractConsoleProxyBackend.java (1)

122-127: 错误消息存在语法问题。

根据编码规范,错误消息应使用正确的英文。当前消息存在以下语法问题:

  • "it's mostly like" → "it's most likely that"
  • "please wait for the console agent connected" → "please wait for the console agent to connect"
建议修复
         if (!isAgentConnected()) {
-            complete.fail(operr(ORG_ZSTACK_CONSOLE_10011, "the console agent is not connected; it's mostly like the management node just starts, " +
-                    "please wait for the console agent connected, or you can reconnect it manually if disconnected for a long time."
+            complete.fail(operr(ORG_ZSTACK_CONSOLE_10011, "the console agent is not connected; it's most likely that the management node just started, " +
+                    "please wait for the console agent to connect, or you can reconnect it manually if disconnected for a long time."
             ));
             return;
         }
plugin/applianceVm/src/main/java/org/zstack/appliancevm/ApplianceVmConnectFlow.java (1)

144-150: 变量命名与静态导入方法冲突

局部变量 err 与第 32 行静态导入的 Platform.err 方法同名,虽然 Java 编译器可以正确处理,但这违反了编码规范中关于避免同名变量的要求,容易造成混淆。

根据编码规范:"避免在父子类或同一代码块中出现相同名字的成员或局部变量,防止混淆。"

建议重命名变量
-                    ErrorCode err = e1 instanceof OperationFailureException ? ((OperationFailureException)e1).getErrorCode() : err(ORG_ZSTACK_APPLIANCEVM_10004, ApplianceVmErrors.UNABLE_TO_START, e1.getMessage());
-                    chain.fail(err);
+                    ErrorCode errorCode = e1 instanceof OperationFailureException ? ((OperationFailureException)e1).getErrorCode() : err(ORG_ZSTACK_APPLIANCEVM_10004, ApplianceVmErrors.UNABLE_TO_START, e1.getMessage());
+                    chain.fail(errorCode);
plugin/sdnController/src/main/java/org/zstack/sdnController/SdnControllerBase.java (1)

486-491: 任务链未正确推进导致潜在死锁

当 vtepIp 冲突检测失败时,调用 completion.fail()return,但没有调用 chain.next()。这会导致 SyncTaskChain 无法继续执行后续任务,可能造成任务链阻塞。

对比同文件中其他类似的失败处理(如 Line 502-505),都在 completion.fail() 后调用了 chain.next()

建议修复
                 if (refvo != null) {
                     completion.fail(argerr(ORG_ZSTACK_SDNCONTROLLER_10010, "could not add host[uuid:%s] to sdn controller[uuid:%s], " +
                                     " because vtepip is used by host[uuid:%s]", msg.getHostUuid(),
                             msg.getSdnControllerUuid(), refvo.getHostUuid()));
+                    chain.next();
                     return;
                 }
plugin/eip/src/main/java/org/zstack/network/service/eip/EipApiInterceptor.java (1)

119-131: 错误消息与实际条件不匹配。

!found 为 true 时(即 IP 未找到/未挂载),错误消息却说 "ip [uuid:%s] is attached to vm nic [%s]"。逻辑上应该是 "ip [uuid:%s] is not attached to vm nic [%s]"。

🐛 建议修复
     if (!found) {
-        throw new ApiMessageInterceptionException(argerr(ORG_ZSTACK_NETWORK_SERVICE_EIP_10007, "ip [uuid:%s] is attached to vm nic [%s]", guestIpUuid, vmNicUuid));
+        throw new ApiMessageInterceptionException(argerr(ORG_ZSTACK_NETWORK_SERVICE_EIP_10007, "ip [uuid:%s] is not attached to vm nic [uuid:%s]", guestIpUuid, vmNicUuid));
     }
compute/src/main/java/org/zstack/compute/host/HostManagerImpl.java (1)

484-503: 架构检查错误码更新合理。

两个架构相关的错误场景使用了正确的错误码:

  • 架构为空 → ORG_ZSTACK_COMPUTE_HOST_10105
  • 架构不匹配 → ORG_ZSTACK_COMPUTE_HOST_10106

小问题:Line 500 的错误消息 "not match" 语法不太准确,建议改为 "does not match"

📝 建议修复语法
-                    trigger.fail(operr(ORG_ZSTACK_COMPUTE_HOST_10106, "cluster[uuid:%s]'s architecture is %s, not match the host[name:%s, ip:%s] architecture %s",
+                    trigger.fail(operr(ORG_ZSTACK_COMPUTE_HOST_10106, "cluster[uuid:%s]'s architecture is %s, does not match the host[name:%s, ip:%s] architecture %s",
plugin/loadBalancer/src/main/java/org/zstack/network/service/lb/LoadBalancerApiInterceptor.java (1)

1076-1124: 多处拼写错误: "cloud not" 应为 "could not"。

在创建监听器的验证逻辑中,多个错误消息存在相同的拼写错误:

  • 第 1076 行: "cloud not change the loadbalancer listener"
  • 第 1082 行: "cloud not create the loadbalancer listener"
  • 第 1092 行: "cloud not create the loadbalancer listener"
  • 第 1097 行: "cloud not create the loadbalancer listener"
  • 第 1102 行: "cloud not create the loadbalancer listener"
  • 第 1118 行: "cloud not create the loadbalancer listener"
  • 第 1124 行: "cloud not create the loadbalancer listener"

建议批量替换 "cloud not""could not"

core/src/main/java/org/zstack/core/encrypt/EncryptFacadeImpl.java (1)

216-232: 抛出的异常会被同一 try-catch 块捕获并静默处理。

在第 221 行抛出的 OperationFailureException 会被第 230-232 行的 catch (Exception e) 捕获,导致加密失败仅以 debug 级别记录日志,然后继续处理下一条记录,而不是真正中断操作。

如果期望加密失败时中断整个操作,应将异常抛出逻辑移到 catch 块外部,或者单独捕获 OperationFailureException 并重新抛出。

🐛 建议修复方案
                             try {
                                 String decryptedString = decrypt(encryptedString);
                                 ErrorableValue<String> encrypt = encrypt(decryptedString, key);
                                 if (encrypt.error != null) {
                                     logger.error(String.format("Encryption error : %s", encrypt.error));
                                     throw new OperationFailureException(operr(ORG_ZSTACK_CORE_ENCRYPT_10000, "Encryption error : %s", encrypt.error));
                                 }

                                 String sql = String.format("update %s set %s = :encrypted where uuid = :uuid", className, field.getName());

                                 Query query = dbf.getEntityManager().createQuery(sql);
                                 query.setParameter("encrypted", encrypt.result);
                                 query.setParameter("uuid", uuid);
                                 query.executeUpdate();
+                            } catch (OperationFailureException e) {
+                                throw e;
                             } catch (Exception e) {
                                 logger.debug(String.format("decrypt[className: %s, uuid: %s] origin password error because : %s", className, uuid, e.getMessage()));
                             }
compute/src/main/java/org/zstack/compute/vm/VmInstanceBase.java (1)

2895-2923: 严重:错误分支使用了未初始化的 r.getError(),会导致 NPE/丢失真实错误
if (!reply.isSuccess()) { r.setError(err(..., r.getError(), r.getError().getDetails())); }r.getError() 初始为 null,应使用 reply.getError()。这会直接影响失败返回的正确性。

建议修复
                 AttachNicToVmReply r = new AttachNicToVmReply();
                 if (!reply.isSuccess()) {
-                    r.setError(err(ORG_ZSTACK_COMPUTE_VM_10274, VmErrors.ATTACH_NETWORK_ERROR, r.getError(), r.getError().getDetails()));
+                    r.setError(err(ORG_ZSTACK_COMPUTE_VM_10274, VmErrors.ATTACH_NETWORK_ERROR,
+                            reply.getError(), reply.getError().getDetails()));
                 }
                 bus.reply(msg, r);
plugin/ceph/src/main/java/org/zstack/storage/ceph/primary/CephPrimaryStorageBase.java (1)

4331-4394: 新增错误码聚合:OK;但修正英文文案拼写(ceph)

  • ORG_10015/10016 聚合错误码做得很好。
  • ORG_10017 文案里 "cep cluster" 应为 "ceph cluster"
建议修复
- errors.add(operr(ORG_ZSTACK_STORAGE_CEPH_PRIMARY_10017, "the mon[ip:%s] returns a fsid[%s] different from the current fsid[%s] of the cep cluster," +
+ errors.add(operr(ORG_ZSTACK_STORAGE_CEPH_PRIMARY_10017, "the mon[ip:%s] returns a fsid[%s] different from the current fsid[%s] of the ceph cluster," +
         "are you adding a mon not belonging to current cluster mistakenly?", base.getSelf().getHostname(), fsid, getSelf().getFsid())
 );
plugin/ceph/src/main/java/org/zstack/storage/ceph/CephApiInterceptor.java (1)

111-139: Interceptor 入参建议统一 trim(含 monUrls/poolName/hostname),否则复制粘贴空白符会绕过/误伤校验

当前对 monUrls / poolName / hostname 的校验均基于原始字符串;用户从浏览器复制可能带空格/换行,导致:

  • distinctMons 去重失效("1.1.1.1" vs "1.1.1.1 "
  • MonUri 解析失败但提示不直观
  • isEmpty() 判断不命中(全空格但非 empty)

建议在各 validate 开头做 trim 归一化(尤其是 checkMonUrls 入口)。

建议修复(示例:集中在 checkMonUrls 入口处理)
 private void checkMonUrls(List<String> monUrls) {
+    if (monUrls != null) {
+        for (int i = 0; i < monUrls.size(); i++) {
+            String v = monUrls.get(i);
+            if (v != null) {
+                monUrls.set(i, v.trim());
+            }
+        }
+    }
     distinctMons(monUrls);

     for (String monUrl : monUrls) {
         String url = String.format("ssh://%s", monUrl);
         try {
             new MonUri(url);
         } catch (OperationFailureException ae) {
             throw new ApiMessageInterceptionException(ae.getErrorCode());
         } catch (Exception e) {
             logger.warn(e.getMessage(), e);
             throw new ApiMessageInterceptionException(argerr(ORG_ZSTACK_STORAGE_CEPH_10015, "invalid monUrl[%s]. A valid url is in format of %s", monUrl, MON_URL_FORMAT));
         }
     }
 }
As per coding guidelines, `*Interceptor.java` 需要对来自 Message 的参数做 trim。

Also applies to: 190-204, 206-225, 245-254

plugin/kvm/src/main/java/org/zstack/kvm/KVMHost.java (1)

2728-2734: 修正两处“错误信息与变量不匹配”问题(现在被错误码固化后更应准确)

  1. Line 2763-2765:提示 vm[uuid:%s] 但传入 volume.getUuid()(volume uuid)。应改为 volume.getVmInstanceUuid()(在该分支里已确保非空)。
  2. Line 3040-3043:SERIOUS BUG 分支使用 Platform.inerr(...) 很好(仅提醒:该异常在异步回调里直接 throw,确认上层能正确处理/落库即可)。
建议修复 diff(Line 2763-2765)
-                throw new OperationFailureException(operr(ORG_ZSTACK_KVM_10054, "cannot do volume snapshot merge when vm[uuid:%s] is in state of %s." +
-                                " The operation is only allowed when vm is Running or Stopped", volume.getUuid(), state));
+                throw new OperationFailureException(operr(ORG_ZSTACK_KVM_10054, "cannot do volume snapshot merge when vm[uuid:%s] is in state of %s." +
+                                " The operation is only allowed when vm is Running or Stopped", volume.getVmInstanceUuid(), state));

Also applies to: 2763-2765, 2770-2773, 2790-2796, 2865-2866, 2902-2904, 2982-2983, 2987-2994, 3040-3043, 3050-3053

🤖 Fix all issues with AI agents
In @compute/src/main/java/org/zstack/compute/zone/ZoneBase.java:
- Around line 240-246: The err() call in the FlowErrorHandler.handle is using
the wrong parameter order; update the invocation to match the Platform.err(Enum
errCode, ErrorCode cause, String fmt, Object... args) signature by passing
SysErrors.DELETE_RESOURCE_ERROR as the first argument, the current errCode as
the second (cause), then ORG_ZSTACK_COMPUTE_ZONE_10002 as the fmt (or formatted
message key), and errCode.getDetails() as the vararg; modify the call inside the
anonymous FlowErrorHandler (handle method) where evt.setError(err(...)) is
invoked and keep publishing evt via bus.publish(evt).

In
@plugin/ceph/src/main/java/org/zstack/storage/ceph/primary/CephPrimaryStorageBase.java:
- Around line 1799-1803: The call to
operr(ORG_ZSTACK_STORAGE_CEPH_PRIMARY_10002, details) in CephPrimaryStorageBase
(after trash.makeSureInstallPathNotUsed(inv)) treats an external string as a
format template and can throw when details contains '%'—change the call to pass
a format specifier and the value, e.g.
operr(ORG_ZSTACK_STORAGE_CEPH_PRIMARY_10002, "%s", details), and make the same
change for the similar operr usage around the 1894-1899 area; update the
inv.getResourceUuid()/inv.getTrashId() completion.success invocation to use the
new operr call signature.

In
@plugin/flatNetworkProvider/src/main/java/org/zstack/network/service/flat/FlatDhcpBackend.java:
- Around line 2494-2500: In FlatDhcpBackend.java inside the validation block
that checks msg.getDhcpv6ServerIp(), replace the incorrect error variable
msg.getDhcpServerIp() with msg.getDhcpv6ServerIp() in the
ApiMessageInterceptionException so the thrown error reports the actual IPv6 DHCP
server IP; update the argerr call accordingly where the UsedIpVO check and
exception are raised.
- Line 2182: The operr() call in FlatDhcpBackend currently passes rsp.getError()
as an extra first argument (operr(ORG_ZSTACK_NETWORK_SERVICE_FLAT_10028,
rsp.getError(), "operation error, because:%s", rsp.getError())), which is
inconsistent with other usages; change it to match the pattern used elsewhere by
calling operr(ORG_ZSTACK_NETWORK_SERVICE_FLAT_10028, "operation error,
because:%s", rsp.getError()) so only the format string and the error parameter
are supplied (keep ORG_ZSTACK_NETWORK_SERVICE_FLAT_10028 and rsp.getError() as
the format arg).

In @plugin/ldap/src/main/java/org/zstack/ldap/LdapApiInterceptor.java:
- Around line 127-131: The format string in LdapApiInterceptor's
ApiMessageInterceptionException call is wrong ("[%,%s]" has an invalid
specifier) and mismatches the three arguments; update the argerr invocation
(ORG_ZSTACK_LDAP_10010) to use a correct format string such as "Wrong
LdapServerType[%s], valid values: [%s,%s]" so the placeholders align with the
provided values (type, LdapConstant.OpenLdap.TYPE, LdapConstant.WindowsAD.TYPE).

In
@plugin/localstorage/src/main/java/org/zstack/storage/primary/local/LocalStorageKvmBackend.java:
- Around line 3798-3802: In the LocalStorageKvmBackend anonymous fail(ErrorCode
errorCode) handler, avoid calling errorCode.getCause().getDetails() directly
because getCause() may be null; instead build the message null-safely (e.g., use
errorCode.getCause() != null ? errorCode.getCause().getDetails() :
errorCode.getDetails()) when calling completion.fail(operr(...)) so the failure
path never throws an NPE; apply the same null-safe pattern to the other similar
fail handlers (the one around makeInitializedFilePath() / hostUuid and the
occurrence noted at the other block).

In
@plugin/nfsPrimaryStorage/src/main/java/org/zstack/storage/primary/nfs/NfsPrimaryStorageKVMBackend.java:
- Around line 1258-1260: The error message format string in the completion.fail
call for operr(ORG_ZSTACK_STORAGE_PRIMARY_NFS_10036, ...) mistakenly contains
the literal text "installPath, pinv.getUuid(), rsp.getError()"; remove that
literal and use proper format placeholders (e.g., %s) matching the passed
arguments (installPath, pinv.getUuid(), rsp.getError()) so the log shows the
actual values when completion.fail is invoked in NfsPrimaryStorageKVMBackend.
🟡 Minor comments (39)
compute/src/main/java/org/zstack/compute/vm/VmAllocateCdRomFlow.java-57-57 (1)

57-57: 错误信息英文语法不规范。

根据编码规范,错误信息应使用正确的英文。"cdRom deviceId repetition" 语法不通顺,建议修改为更自然的表达。

📝 建议修改
-            trigger.fail(operr(ORG_ZSTACK_COMPUTE_VM_10080, "vm[uuid:%s] cdRom deviceId repetition",spec.getVmInventory().getUuid()));
+            trigger.fail(operr(ORG_ZSTACK_COMPUTE_VM_10080, "vm[uuid:%s] has duplicate cdRom deviceId", spec.getVmInventory().getUuid()));
core/src/main/java/org/zstack/core/ansible/PrepareAnsible.java-37-39 (1)

37-39: 修复错误消息的语法问题。

根据编码规范,错误消息应使用正确的英文。"fail to create" 应改为 "failed to create"(过去时态描述已发生的失败)。

✏️ 建议修复
-                throw new OperationFailureException(operr(ORG_ZSTACK_CORE_ANSIBLE_10001, "fail to create new File[%s]", hostsFile));
+                throw new OperationFailureException(operr(ORG_ZSTACK_CORE_ANSIBLE_10001, "failed to create new File[%s]", hostsFile));
image/src/main/java/org/zstack/image/BackupStorageDeleteBitGC.java-36-37 (1)

36-37: 错误消息英文语法有瑕疵。

根据编码规范,错误消息应使用正确的英文。"is not in status of Connected" 语法略显生硬,建议修改为更自然的表达:

📝 建议修复
-            completion.fail(operr(ORG_ZSTACK_IMAGE_10021, "the backup storage[uuid:%s] is not in status of" +
-                    " Connected, current status is %s", backupStorageUuid, bsStatus));
+            completion.fail(operr(ORG_ZSTACK_IMAGE_10021, "the backup storage[uuid:%s] is not in Connected status," +
+                    " current status is %s", backupStorageUuid, bsStatus));
plugin/localstorage/src/main/java/org/zstack/storage/primary/local/LocalStorageSnapshotDeletionProtector.java-26-28 (1)

26-28: 错误消息存在语法错误。

根据编码规范,报错信息应当使用正确的、无拼写错误的英文。当前消息 "seems not belong" 语法不正确,应修改为 "does not seem to belong"

📝 建议的修改
-            completion.fail(inerr(ORG_ZSTACK_STORAGE_PRIMARY_LOCAL_10019, "the snapshot[name:%s, uuid:%s, path: %s] seems not belong to the volume[uuid:%s]",
+            completion.fail(inerr(ORG_ZSTACK_STORAGE_PRIMARY_LOCAL_10019, "the snapshot[name:%s, uuid:%s, path: %s] does not seem to belong to the volume[uuid:%s]",
identity/src/main/java/org/zstack/identity/DefaultAuthorizationBackend.java-67-67 (1)

67-67: 错误消息存在语法问题:主谓不一致。

"the operations[%s] is denied"operations 是复数,但 is 是单数形式。应修正为 are 以保持语法正确。

🐛 建议修复
-            throw new OperationFailureException(operr(ORG_ZSTACK_IDENTITY_10005, "the operations[%s] is denied", deniedApis));
+            throw new OperationFailureException(operr(ORG_ZSTACK_IDENTITY_10005, "the operations[%s] are denied", deniedApis));
core/src/main/java/org/zstack/core/trash/StorageRecycleImpl.java-145-147 (1)

145-147: 错误信息存在语法问题。

错误消息 "non support resourceType to create trash" 语法不正确。根据编码规范,代码中应使用正确的英文。

建议修复
-            throw new OperationFailureException(inerr(ORG_ZSTACK_CORE_TRASH_10001, "non support resourceType to create trash"));
+            throw new OperationFailureException(inerr(ORG_ZSTACK_CORE_TRASH_10001, "unsupported resourceType to create trash"));
core/src/main/java/org/zstack/core/config/GlobalConfig.java-429-432 (1)

429-432: 错误码使用正确,但错误消息的英文语法需要改进。

根据编码规范,错误消息应使用正确的英文。当前消息 "do not allow skip verification" 语法不够规范。

建议修改
     public void updateValueSkipValidation(Object val) {
         if (!CoreGlobalProperty.UNIT_TEST_ON) {
-            throw new OperationFailureException(operr(ORG_ZSTACK_CORE_CONFIG_10004, "do not allow skip verification"));
+            throw new OperationFailureException(operr(ORG_ZSTACK_CORE_CONFIG_10004, "skipping verification is not allowed outside of unit tests"));
         }
compute/src/main/java/org/zstack/compute/allocator/HostSortorChain.java-136-138 (1)

136-138: 错误消息中 e.getMessage() 重复出现。

格式化字符串中有三个占位符,但 e.getMessage() 被使用了两次,导致错误信息重复输出。建议移除末尾重复的 %s 和对应参数。

🐛 建议修复
                            trigger.fail(operr(
-                            ORG_ZSTACK_COMPUTE_ALLOCATOR_10034,         "[Host Allocation]: %s on host[uuid:%s]. try next one. %s", e.getMessage(), host.getUuid(), e.getMessage()));
+                            ORG_ZSTACK_COMPUTE_ALLOCATOR_10034, "[Host Allocation]: %s on host[uuid:%s]. try next one.", e.getMessage(), host.getUuid()));
plugin/ceph/src/main/java/org/zstack/storage/ceph/backup/CephBackupStorageMonBase.java-104-107 (1)

104-107: 错误消息中句子之间缺少空格。

字符串拼接导致 "deleted.This" 中缺少空格。

🔧 建议修复
             throw new OperationFailureException(
-                    operr(ORG_ZSTACK_STORAGE_CEPH_BACKUP_10012, "cannot update status of the ceph backup storage mon[uuid:%s], it has been deleted." +
-                            "This error can be ignored", uuid)
+                    operr(ORG_ZSTACK_STORAGE_CEPH_BACKUP_10012, "cannot update status of the ceph backup storage mon[uuid:%s], it has been deleted. " +
+                            "This error can be ignored", uuid)
             );
compute/src/main/java/org/zstack/compute/allocator/AvoidHostAllocatorFlow.java-30-31 (1)

30-31: 错误消息存在语法问题,需修正。

根据编码规范,错误消息应使用正确的英文。当前消息有以下语法问题:

  1. "after rule out" 应为 "after ruling out"(介词后需用动名词)
  2. "avoided host%s" 应为 "avoided hosts %s"(复数形式,且格式化符前加空格更清晰)
建议修复
-            fail(Platform.operr(ORG_ZSTACK_COMPUTE_ALLOCATOR_10026, "after rule out avoided host%s, there is no host left in candidates", spec.getAvoidHostUuids()));
+            fail(Platform.operr(ORG_ZSTACK_COMPUTE_ALLOCATOR_10026, "after ruling out avoided hosts %s, no candidate host remains", spec.getAvoidHostUuids()));
plugin/loginPlugin/src/main/java/org/zstack/login/plugin/LoginPluginBackend.java-64-66 (1)

64-66: 格式化字符串与参数不匹配。

错误消息 "missing LoginUserInfo when use plugin login" 中没有 %s 占位符,但代码传递了 loginContext.getLoginPluginName() 作为格式化参数。该参数将被忽略,导致错误信息缺少有价值的上下文。

建议在消息中添加占位符以包含插件名称:

🐛 修复格式化字符串
-            completion.fail(operr(ORG_ZSTACK_LOGIN_PLUGIN_10002, "missing LoginUserInfo when use plugin login", loginContext.getLoginPluginName()));
+            completion.fail(operr(ORG_ZSTACK_LOGIN_PLUGIN_10002, "missing LoginUserInfo when use plugin login [%s]", loginContext.getLoginPluginName()));
plugin/flatNetworkProvider/src/main/java/org/zstack/network/service/flat/FlatDHCPDeleteNamespaceGC.java-38-38 (1)

38-38: 错误消息的英文表达需要改进

根据编码规范,报错应当使用正确的、无语法错误的英文。当前消息 "operation error, because:%s" 存在以下问题:

  • 冒号后缺少空格
  • 句首未大写
  • "because" 用于技术错误消息不够简洁

建议使用更清晰、符合语法的表达。

📝 建议的修改
-                    return rsp.isSuccess() ? null : operr(ORG_ZSTACK_NETWORK_SERVICE_FLAT_10013, "operation error, because:%s", rsp.getError());
+                    return rsp.isSuccess() ? null : operr(ORG_ZSTACK_NETWORK_SERVICE_FLAT_10013, "Failed to delete namespace: %s", rsp.getError());
plugin/localstorage/src/main/java/org/zstack/storage/primary/local/LocalStorageAllocatorFactory.java-385-386 (1)

385-386: 错误消息大小写问题。

根据编码规范,"invalid uri" 应改为 "Invalid URI":句首应大写,URI 作为缩写词应全部大写。

📝 建议修复
-                        argerr(ORG_ZSTACK_STORAGE_PRIMARY_LOCAL_10023, "invalid uri, correct example is file://$URL;hostUuid://$HOSTUUID or volume://$VOLUMEUUID "));
+                        argerr(ORG_ZSTACK_STORAGE_PRIMARY_LOCAL_10023, "Invalid URI, correct example is file://$URL;hostUuid://$HOSTUUID or volume://$VOLUMEUUID"));
plugin/localstorage/src/main/java/org/zstack/storage/primary/local/LocalStorageAllocatorFactory.java-203-206 (1)

203-206: 错误消息存在语法问题。

根据编码规范,错误消息应使用正确的英文表达。当前 "the host is either not having enough" 语法不够规范,建议改为 "the host either does not have enough"

📝 建议修复
-                    throw new OperationFailureException(err(ORG_ZSTACK_STORAGE_PRIMARY_LOCAL_10021, HostAllocatorError.NO_AVAILABLE_HOST,
-                            "the vm[uuid: %s] using local primary storage can only be started on the host[uuid: %s], but the host is either not having enough CPU/memory/GPU/VFNIC or in" +
-                                    " the state[Enabled] or status[Connected] to start the vm", vm.getUuid(), hostUuid
-                    ));
+                    throw new OperationFailureException(err(ORG_ZSTACK_STORAGE_PRIMARY_LOCAL_10021, HostAllocatorError.NO_AVAILABLE_HOST,
+                            "the vm[uuid: %s] using local primary storage can only be started on the host[uuid: %s], but the host either does not have enough CPU/memory/GPU/VFNIC or is not in" +
+                                    " the state[Enabled] or status[Connected] to start the vm", vm.getUuid(), hostUuid
+                    ));
core/src/main/java/org/zstack/core/errorcode/ElaborationManagerImpl.java-108-116 (1)

108-116: 错误消息语法问题

Line 113 的错误消息语法不正确:

  • "is not existed" → 应为 "does not exist"
  • "is empty folder" → 应为 "is an empty folder"
📝 建议修复
-                                trigger.fail(argerr(ORG_ZSTACK_CORE_ERRORCODE_10003, "%s is not existed or is empty folder", filename));
+                                trigger.fail(argerr(ORG_ZSTACK_CORE_ERRORCODE_10003, "%s does not exist or is an empty folder", filename));
core/src/main/java/org/zstack/core/errorcode/ElaborationManagerImpl.java-249-252 (1)

249-252: 错误消息语法问题

"elaboration code must be number!" 缺少冠词,且感叹号在错误消息中不常见。

📝 建议修复
-                                    trigger.fail(operr(ORG_ZSTACK_CORE_ERRORCODE_10005, "elaboration code must be number!"));
+                                    trigger.fail(operr(ORG_ZSTACK_CORE_ERRORCODE_10005, "elaboration code must be a number"));
core/src/main/java/org/zstack/core/errorcode/ElaborationManagerImpl.java-70-79 (1)

70-79: 错误消息存在语法问题

根据编码规范,错误消息应使用正确的英文。当前消息存在语法错误:

  • Line 72: "non file or jsoncontent input" → 应为 "no file or jsonContent input"
  • Line 77: "file or jsoncontent cannot both nonempty" → 应为 "file and jsonContent cannot both be non-empty"
📝 建议修复
     private void preCheckElaborationContent(String filename, String jsonContent, ReturnValueCompletion<List<ElaborationCheckResult>> completion) {
         if (filename == null && jsonContent == null) {
-            completion.fail(argerr(ORG_ZSTACK_CORE_ERRORCODE_10000, "non file or jsoncontent input"));
+            completion.fail(argerr(ORG_ZSTACK_CORE_ERRORCODE_10000, "no file or jsonContent input"));
             return;
         }

         if (filename != null && jsonContent != null) {
-            completion.fail(argerr(ORG_ZSTACK_CORE_ERRORCODE_10001, "file or jsoncontent cannot both nonempty"));
+            completion.fail(argerr(ORG_ZSTACK_CORE_ERRORCODE_10001, "file and jsonContent cannot both be non-empty"));
             return;
         }
identity/src/main/java/org/zstack/identity/QuotaUtil.java-35-38 (1)

35-38: 格式错误:import 语句与 Javadoc 注释错误地连接在一起。

第 35 行的 import 语句末尾直接连接了 /**,导致 Javadoc 注释与 import 语句在同一行。这是一个格式问题,需要在 import 语句和 Javadoc 注释之间添加空行。

🔧 建议修复
-import static org.zstack.utils.clouderrorcode.CloudOperationsErrorCode.*;/**
+import static org.zstack.utils.clouderrorcode.CloudOperationsErrorCode.*;
+
+/**
  * Created by miao on 16-10-9.
  */
-
image/src/main/java/org/zstack/image/ImageApiInterceptor.java-181-182 (1)

181-182: 建议修正错误信息中的中文标点符号。

错误信息中使用了中文逗号 ,根据编码规范,代码中不应包含中文字符(包括报错、注释等)。

🔧 建议修复
-            throw new ApiMessageInterceptionException(argerr(ORG_ZSTACK_IMAGE_10011, "url must starts with 'file:///', 'http://', 'https://', 'ftp://', 'sftp://' or '/'"));
+            throw new ApiMessageInterceptionException(argerr(ORG_ZSTACK_IMAGE_10011, "url must starts with 'file:///', 'http://', 'https://', 'ftp://', 'sftp://' or '/'"));
network/src/main/java/org/zstack/network/l3/L3NetworkApiInterceptor.java-141-144 (1)

141-144: 错误消息中的参数描述错误。

此处验证的是 msg.getEndIp(),但错误消息中却说 "start ip[%s]",应该改为 "end ip[%s]"。

🐛 建议修复
         if (!NetworkUtils.isValidIPAddress(msg.getEndIp())) {
-            throw new ApiMessageInterceptionException(argerr(ORG_ZSTACK_NETWORK_L3_10002, "could not reserve ip range, " +
-                    "because start ip[%s] is not valid ip address", msg.getStartIp()));
+            throw new ApiMessageInterceptionException(argerr(ORG_ZSTACK_NETWORK_L3_10002, "could not reserve ip range, " +
+                    "because end ip[%s] is not valid ip address", msg.getEndIp()));
         }
network/src/main/java/org/zstack/network/l3/L3NetworkApiInterceptor.java-128-130 (1)

128-130: 错误消息存在语法错误。

错误消息 "could delete ip address" 应该是 "could not delete ip address" 或 "cannot delete ip address"。

🐛 建议修复
-                throw new ApiMessageInterceptionException(argerr(ORG_ZSTACK_NETWORK_L3_10000, "could delete ip address, " +
+                throw new ApiMessageInterceptionException(argerr(ORG_ZSTACK_NETWORK_L3_10000, "cannot delete ip address, " +
                         "because it's used by vmnic[uuid:%s]", vo.getVmNicUuid()));
plugin/sdnController/src/main/java/org/zstack/sdnController/SdnControllerApiInterceptor.java-188-195 (1)

188-195: 错误消息与条件逻辑不一致

Line 192 检查 msg.getVtepIp() != null && msg.getNetmask() == null,表示"指定了 vtepIp 但未指定 netmask"。但 Line 193-194 的错误消息说 "because netmask is specified"(netmask 已指定),这与实际条件相矛盾。应该是 "because netmask is not specified"。

建议修复
         if (msg.getVtepIp() != null && msg.getNetmask() == null) {
             throw new ApiMessageInterceptionException(argerr(ORG_ZSTACK_SDNCONTROLLER_10023, "could not change host[uuid:%s] of sdn controller[uuid:%s], " +
-                    " because netmask is specified", msg.getHostUuid(), msg.getSdnControllerUuid()));
+                    " because netmask is not specified", msg.getHostUuid(), msg.getSdnControllerUuid()));
         }
plugin/sdnController/src/main/java/org/zstack/sdnController/h3cVcfc/H3cVcfcApiInterceptor.java-235-237 (1)

235-237: 错误消息格式问题

错误消息中 "can not" 应为 "cannot"(单词),且 "l3 network" 后缺少空格导致拼接问题。

建议修复
-            throw new ApiMessageInterceptionException(argerr(ORG_ZSTACK_SDNCONTROLLER_H3CVCFC_10022, "can not create l3 network" +
-                    "because H3C VCFC V2 SDN controller does not support l3[type:%s, category:%s]", msg.getType(), msg.getCategory()));
+            throw new ApiMessageInterceptionException(argerr(ORG_ZSTACK_SDNCONTROLLER_H3CVCFC_10022, "cannot create L3 network " +
+                    "because H3C VCFC V2 SDN controller does not support l3[type:%s, category:%s]", msg.getType(), msg.getCategory()));
plugin/flatNetworkProvider/src/main/java/org/zstack/network/service/flat/FlatDhcpBackend.java-2382-2383 (1)

2382-2383: 错误信息语法错误:"could" 应为 "could not"

此处及后续多个位置的错误信息使用了 "could" 而非 "could not"。这些是验证失败时抛出的异常,应表达"无法执行"的含义。

根据编码规范,错误信息应使用正确的英文。受影响的行包括:2382, 2391, 2419, 2424, 2430, 2435, 2444, 2451, 2456, 2465, 2470, 2476, 2481, 2489, 2497。

示例修复(第 2382 行)
-throw new ApiMessageInterceptionException(argerr(ORG_ZSTACK_NETWORK_SERVICE_FLAT_10037, "could delete ip address, " +
+throw new ApiMessageInterceptionException(argerr(ORG_ZSTACK_NETWORK_SERVICE_FLAT_10037, "could not delete ip address, " +
         "because ip [%s] is dhcp server ip", vo.getIp()));

其他位置同理,将 "could" 改为 "could not"。

plugin/sdnController/src/main/java/org/zstack/sdnController/h3cVcfc/H3cVcfcSdnController.java-160-162 (1)

160-162: 异常信息丢失,不利于问题排查。

此处 catch 块使用了固定的通用消息 "because of a communication error",丢失了实际的异常详情。其他类似的 catch 块(如第 122、334、419 行)都包含了 e.getMessage() 以保留异常信息。建议保持一致性,便于运维排查问题。

建议修复
         } catch (Exception e) {
-            completion.fail(operr(ORG_ZSTACK_SDNCONTROLLER_H3CVCFC_10030, "Could not retrieve default tenant from SDN controller [ip:%s] because of a communication error", self.getIp()));
+            completion.fail(operr(ORG_ZSTACK_SDNCONTROLLER_H3CVCFC_10030, "Could not retrieve default tenant from SDN controller [ip:%s] because %s", self.getIp(), e.getMessage()));
         }
plugin/loadBalancer/src/main/java/org/zstack/network/service/lb/LoadBalancerApiInterceptor.java-1509-1510 (1)

1509-1510: 错误消息格式问题。

第 1510 行的错误消息格式不完整,缺少占位符参数:

throw new ApiMessageInterceptionException(argerr(ORG_ZSTACK_NETWORK_SERVICE_LB_10134, "could not add backend server vmnic to serverGroup[uuid:%s] ,because vmnic weight[%s] not a correct number",vmNic.get("weight")));

消息模板有两个 %s 占位符,但只传入了一个参数 vmNic.get("weight")。应该传入 msg.getServerGroupUuid()vmNic.get("weight")

📝 建议的修复
-                        throw new ApiMessageInterceptionException(argerr(ORG_ZSTACK_NETWORK_SERVICE_LB_10134, "could not add backend server vmnic to serverGroup[uuid:%s] ,because vmnic weight[%s] not a correct number",vmNic.get("weight")));
+                        throw new ApiMessageInterceptionException(argerr(ORG_ZSTACK_NETWORK_SERVICE_LB_10134, "could not add backend server vmnic to serverGroup[uuid:%s] ,because vmnic weight[%s] not a correct number", msg.getServerGroupUuid(), vmNic.get("weight")));
plugin/loadBalancer/src/main/java/org/zstack/network/service/lb/LoadBalancerApiInterceptor.java-1832-1834 (1)

1832-1834: 错误消息格式参数缺失。

第 1833 行存在与前述相同的格式问题,消息模板有两个占位符但只传入了一个参数。

📝 建议的修复
-                            throw new ApiMessageInterceptionException(argerr(ORG_ZSTACK_NETWORK_SERVICE_LB_10162, "could not change backend server vmnic to serverGroup[uuid:%s] ,because vmnic weight[%s] not a correct number",vmNic.get("weight")));
+                            throw new ApiMessageInterceptionException(argerr(ORG_ZSTACK_NETWORK_SERVICE_LB_10162, "could not change backend server vmnic to serverGroup[uuid:%s] ,because vmnic weight[%s] not a correct number", msg.getServerGroupUuid(), vmNic.get("weight")));
plugin/loadBalancer/src/main/java/org/zstack/network/service/lb/LoadBalancerApiInterceptor.java-1591-1593 (1)

1591-1593: 错误消息格式参数缺失。

第 1592 行的错误消息模板有两个占位符,但只传入了一个参数:

throw new ApiMessageInterceptionException(argerr(ORG_ZSTACK_NETWORK_SERVICE_LB_10142, "could not add backend server ip to serverGroup[uuid:%s] ,because vmnic weight[%s] not a correct number",server.get("weight")));
📝 建议的修复
-                        throw new ApiMessageInterceptionException(argerr(ORG_ZSTACK_NETWORK_SERVICE_LB_10142, "could not add backend server ip to serverGroup[uuid:%s] ,because vmnic weight[%s] not a correct number",server.get("weight")));
+                        throw new ApiMessageInterceptionException(argerr(ORG_ZSTACK_NETWORK_SERVICE_LB_10142, "could not add backend server ip to serverGroup[uuid:%s] ,because vmnic weight[%s] not a correct number", msg.getServerGroupUuid(), server.get("weight")));
plugin/loadBalancer/src/main/java/org/zstack/network/service/lb/LoadBalancerApiInterceptor.java-1872-1874 (1)

1872-1874: 错误消息格式参数缺失。

第 1873 行存在相同的格式问题。

📝 建议的修复
-                            throw new ApiMessageInterceptionException(argerr(ORG_ZSTACK_NETWORK_SERVICE_LB_10167, "could not add backend server ip to serverGroup[uuid:%s] ,because vmnic weight[%s] not a correct number",server.get("weight")));
+                            throw new ApiMessageInterceptionException(argerr(ORG_ZSTACK_NETWORK_SERVICE_LB_10167, "could not add backend server ip to serverGroup[uuid:%s] ,because vmnic weight[%s] not a correct number", msg.getServerGroupUuid(), server.get("weight")));
plugin/loadBalancer/src/main/java/org/zstack/network/service/lb/LoadBalancerApiInterceptor.java-280-284 (1)

280-284: 拼写错误: "cloud" 应为 "could"。

根据编码规范,错误消息应使用正确的、无拼写错误的英文。

  • 第 280 行: "cloud not create loadbalancer""could not create loadbalancer"
  • 第 284 行: "cloud not create loadbalancer""could not create loadbalancer"
📝 建议的修复
             if (!IPv6NetworkUtils.isIpv6Address(vipVO.getIp())) {
-                throw new ApiMessageInterceptionException(argerr(ORG_ZSTACK_NETWORK_SERVICE_LB_10035, "cloud not create loadbalancer, because param vipUuid point to VIP[%s] is not ipv4 VIP", vipVO.getUuid()));
+                throw new ApiMessageInterceptionException(argerr(ORG_ZSTACK_NETWORK_SERVICE_LB_10035, "could not create loadbalancer, because param vipUuid point to VIP[%s] is not ipv4 VIP", vipVO.getUuid()));
             }
         } else if (IPv6Constants.IPv6 == vipIpVersion) {
             if (!IPv6NetworkUtils.isIpv6Address(vipVO.getIp())) {
-                throw new ApiMessageInterceptionException(argerr(ORG_ZSTACK_NETWORK_SERVICE_LB_10036, "cloud not create loadbalancer, because param ipv6VipUuid point to VIP[%s] is not ipv6 VIP", vipVO.getUuid()));
+                throw new ApiMessageInterceptionException(argerr(ORG_ZSTACK_NETWORK_SERVICE_LB_10036, "could not create loadbalancer, because param ipv6VipUuid point to VIP[%s] is not ipv6 VIP", vipVO.getUuid()));
             }
         }
plugin/loadBalancer/src/main/java/org/zstack/network/service/lb/LoadBalancerApiInterceptor.java-1365-1396 (1)

1365-1396: 拼写错误: 修改监听器验证中的 "cloud not"。

第 1365、1371、1378、1383、1390、1396 行均存在 "cloud not" 拼写错误,应为 "could not"

plugin/loadBalancer/src/main/java/org/zstack/network/service/lb/LoadBalancerBase.java-776-779 (1)

776-779: 错误:IPv6 VIP 冲突消息中引用了错误的 VIP UUID。

错误信息描述的是 IPv6 VIP 冲突,但格式化参数使用了 self.getVipUuid()(IPv4 VIP UUID),应该使用 self.getIpv6VipUuid()

🐛 修复建议
                 if (!StringUtils.isEmpty(self.getIpv6VipUuid())) {
-                    throw new OperationFailureException(operr(ORG_ZSTACK_NETWORK_SERVICE_LB_10019, "fail to attach ipv6 vip to lb , because lb[%s] has ipv6 vip[%s]", self.getUuid(), self.getVipUuid()));
+                    throw new OperationFailureException(operr(ORG_ZSTACK_NETWORK_SERVICE_LB_10019, "fail to attach ipv6 vip to lb , because lb[%s] has ipv6 vip[%s]", self.getUuid(), self.getIpv6VipUuid()));
                 }
longjob/src/main/java/org/zstack/longjob/LongJobUtils.java-62-65 (1)

62-65: 修复语法错误:应使用 "analyze" 而非 "analysis"。

错误消息中 "analysis the cause" 语法不正确。"analysis" 是名词,应使用动词形式 "analyze"。根据编码规范,报错信息应当使用正确的英文。

📝 建议修复
     public static ErrorCode interruptedErr(String longJobUuid, ErrorCode cause) {
-        return err(ORG_ZSTACK_LONGJOB_10016, LongJobErrors.INTERRUPTED, cause, "some error interrupt long job[uuid:%s]," +
-                " analysis the cause to fix it and resume long job if you want to continue.", longJobUuid);
+        return err(ORG_ZSTACK_LONGJOB_10016, LongJobErrors.INTERRUPTED, cause, "some error interrupted long job[uuid:%s]," +
+                " analyze the cause to fix it and resume long job if you want to continue.", longJobUuid);
     }

另外建议将 "some error interrupt" 改为 "some error interrupted"(过去时态更准确)。

plugin/localstorage/src/main/java/org/zstack/storage/primary/local/LocalStorageKvmBackend.java-2339-2343 (1)

2339-2343: 用户可见错误文案疑似笔误:uuid;%suuid:%s

"the volume[uuid;%s] ..." 这里的分隔符看起来是笔误,会直接影响用户/排障检索。

建议修复
- throw new OperationFailureException(operr(ORG_ZSTACK_STORAGE_PRIMARY_LOCAL_10076, "the volume[uuid;%s] is attached to a VM[uuid:%s] which is in state of %s, cannot do the snapshot merge",
+ throw new OperationFailureException(operr(ORG_ZSTACK_STORAGE_PRIMARY_LOCAL_10076, "the volume[uuid:%s] is attached to a VM[uuid:%s] which is in state of %s, cannot do the snapshot merge",
         volume.getUuid(), volume.getVmInstanceUuid(), state));
compute/src/main/java/org/zstack/compute/vm/VmInstanceApiInterceptor.java-216-217 (1)

216-217: 修复字符串中的换行符错误

错误消息中使用了 /n 而不是 \n,这会导致换行符不生效,错误消息显示异常。

🐛 建议修复
-            String error = "vm nic [%s] attached network services, please detach manually/n" + networkServices.toString();
+            String error = "vm nic [%s] attached network services, please detach manually\n" + networkServices.toString();
compute/src/main/java/org/zstack/compute/vm/VmInstanceBase.java-5115-5117 (1)

5115-5117: 错误文案语法小问题(若不受 i18n 约束建议修正)
"not compatible of the primary storage" 更通顺的是 "not compatible with the primary storage"。如果该文案已进入 i18n/外部依赖,请保持不变。

core/src/main/java/org/zstack/core/Platform.java-1138-1140 (1)

1138-1140: 删除未使用的 experr 方法。

该方法在整个代码库中没有任何调用点,属于死代码,应该从 Platform.java 中移除。

plugin/ceph/src/main/java/org/zstack/storage/ceph/primary/CephPrimaryStorageBase.java-5057-5061 (1)

5057-5061: 修正英文拼写:depreciated → deprecated

错误信息是用户可见文本,建议修正拼写。

建议修复
- reply.setError(operr(ORG_ZSTACK_STORAGE_CEPH_PRIMARY_10028, "backing up snapshots to backup storage is a depreciated feature, which will be removed in future version"));
+ reply.setError(operr(ORG_ZSTACK_STORAGE_CEPH_PRIMARY_10028, "backing up snapshots to backup storage is a deprecated feature, which will be removed in a future version"));
plugin/kvm/src/main/java/org/zstack/kvm/KVMHost.java-6227-6230 (1)

6227-6230: 修正一处超时单位错误(日志/报错会误导运维)

Line 6305:文案是 “%d seconds”,但传入的是毫秒变量 ctimeout(由 TimeUnit.SECONDS.toMillis(timeoutInSec) 得到)。建议改为使用 timeoutInSec(或把文案改成 milliseconds)。

建议修复 diff(Line 6305-6306)
-                                reply.setError(operr(ORG_ZSTACK_KVM_10123, "host[%s] not shutdown in %d seconds", msg.getHostUuid(), ctimeout));
+                                reply.setError(operr(ORG_ZSTACK_KVM_10123, "host[%s] not shutdown in %d seconds", msg.getHostUuid(), timeoutInSec));

Also applies to: 6266-6270, 6305-6308, 6381-6382, 6405-6407, 6469-6473, 6471-6474, 6542-6543, 6739-6740, 6777-6781, 6838-6840, 6904-6905, 6976-6977

Comment on lines 411 to 414
if (vmAttachOtherDiskExtensionPoint == null) {
innerTrigger.fail(operr("the disk does not support attachment. disk type is %s", diskAO.getSourceType()));
innerTrigger.fail(operr(ORG_ZSTACK_COMPUTE_VM_10061, "the disk does not support attachment. disk type is %s", diskAO.getSourceType()));
return;
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

rg "ORG_ZSTACK_COMPUTE_VM_10061" --type java

Repository: MatheMatrix/zstack

Length of output: 180


🔍 Additional research query:

搜索 ZStack CloudOperationsErrorCode 错误码定义,特别是 ORG_ZSTACK_COMPUTE_VM_10061 的定义

💡 Result:

完美!我已经找到了关于 ORG_ZSTACK_COMPUTE_VM_10061 错误码的定义。

ZStack CloudOperationsErrorCode - ORG_ZSTACK_COMPUTE_VM_10061 定义

根据 ZStack 项目的错误处理系统,错误码 10061 的详细定义如下:

属性
分类 VM (虚拟机)
错误码 10061
匹配方式 正则表达式 (regex)
正则表达式 .*failed to start vm\[uuid:.* name:.*\] on kvm host\[uuid:.*, ip:.*\] because:.*failed to get vm\[.*\] qemu-agent ip.*
中文错误信息 启动云主机失败,因为无法获取云主机 [%1$s] 的 qemu-agent IP。
英文错误信息 Failed to start VM, because failed to get vm [%1$s] qemu-agent ip.

错误含义

当 ZStack 系统尝试在 KVM 宿主机上启动虚拟机时,由于无法获取该虚拟机的 qemu-agent IP 地址而失败时,会触发此错误。这通常与虚拟机内部代理服务的网络配置问题有关。

错误处理配置位置

该错误码的定义存储在 ZStack 项目的配置文件中:

  • 路径: conf/errorElaborations/Elaboration.json
  • 特性: 支持国际化 (中英文) 和正则表达式匹配原始错误信息

错误码使用不符

外部知识库显示,错误码 ORG_ZSTACK_COMPUTE_VM_10061 的定义为:"Failed to start VM...failed to get vm qemu-agent ip",用于 KVM 宿主机启动虚拟机时获取 qemu-agent IP 失败的场景。

当前代码使用该错误码表示"磁盘不支持附加",这是错误的错误码引用,会导致用户收到语义不符的错误提示。

需要为"磁盘不支持附加"操作使用正确的错误码常量,或者创建新的错误码定义。请确认使用的错误码是否为正确的。

Comment on lines 240 to 246
}).error(new FlowErrorHandler(msg) {
@Override
public void handle(ErrorCode errCode, Map data) {
evt.setError(err(SysErrors.DELETE_RESOURCE_ERROR, errCode, errCode.getDetails()));
evt.setError(err(ORG_ZSTACK_COMPUTE_ZONE_10002, SysErrors.DELETE_RESOURCE_ERROR, errCode, errCode.getDetails()));
bus.publish(evt);
}
}).start();
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

find . -name "Platform.java" -o -name "*Platform*.java" | head -20

Repository: MatheMatrix/zstack

Length of output: 180


🏁 Script executed:

rg -n "public static.*err\(" --type=java -A 3 | head -50

Repository: MatheMatrix/zstack

Length of output: 180


🏁 Script executed:

rg -n 'err\(ORG_ZSTACK.*SysErrors\.' --type=java -B 2 -A 2 | head -100

Repository: MatheMatrix/zstack

Length of output: 180


🔍 Additional research query:

Search DevinDeepWiki for ZStack Platform class and err() method signature documentation, specifically looking for method definitions in the Platform class that handle error codes with parameters (globalErrorCode, sysError, cause, details)

💡 Result:

Perfect! I have successfully retrieved the complete ZStack Platform documentation from DevinDeepWiki. Let me now provide you with a comprehensive summary of the Platform class and the err() method signatures found in the documentation.

ZStack Platform Class - err() Method Signatures Documentation

Based on the comprehensive DevinDeepWiki documentation for the zstackio/zstack repository, here is the detailed information about the Platform class and its err() method signatures:

Core Platform Class Location

  • File: core/src/main/java/org/zstack/core/Platform.java
  • Purpose: Central utility hub in ZStack for managing global properties, component loading, and error handling

err() Method Signatures

The Platform class provides two main overloaded err() methods with the following signatures:

Signature 1: Basic err() with format string and arguments

public static ErrorCode err(Enum errCode, String fmt, Object...args)

Parameters:

  • errCode (Enum): An Enum representing the global error code
  • fmt (String): A format string for the error details
  • args (Object...): Variable arguments for formatting the details string

Purpose: Creates an ErrorCode with a specified error enumeration, format string, and arguments. The details string is formatted using String.format() or toI18nString() based on the errCode.


Signature 2: err() with cause and format string

public static ErrorCode err(Enum errCode, ErrorCode cause, String fmt, Object...args)

Parameters:

  • errCode (Enum): An Enum representing the global error code
  • cause (ErrorCode): An ErrorCode object that represents the underlying cause of the error
  • fmt (String): A format string for the error details
  • args (Object...): Variable arguments for formatting the details string

Purpose: Allows specifying a cause ErrorCode along with the error enumeration, format string, and arguments. This is used to chain errors and provide more context about the root cause.

Implementation Details

Both methods internally:

  1. Retrieve the ErrorFacade component
  2. Format the details string using String.format() or toI18nString()
  3. Create an ErrorCode object using errf.instantiateErrorCode(errCode, details, cause)
  4. Return the constructed ErrorCode

Convenience Methods

The Platform class also provides several convenience methods that internally call err() with specific SysErrors enums:

Method Internal SysError Purpose
operr() SysErrors.OPERATION_ERROR Creates an operation error
inerr() SysErrors.INTERNAL Creates an internal error
canerr() SysErrors.CANCEL_ERROR Creates a cancel error
argerr() SysErrors.INVALID_ARGUMENT_ERROR Creates an invalid argument error
touterr() SysErrors.TIMEOUT Creates a timeout error
ioerr() SysErrors.IO_ERROR Creates an I/O error
httperr() SysErrors.HTTP_ERROR Creates an HTTP error

Additionally, there are overloaded variants of certain convenience methods that accept a cause parameter (e.g., operr(ErrorCode cause, String fmt, Object...args)).

Error Code Handling

The err() methods handle error elaboration and internationalization automatically through the ErrorFacade component, which:

  • Instantiates ErrorCode objects
  • Applies error elaborations (detailed explanations)
  • Provides internationalized error messages

This comprehensive error handling mechanism ensures that all errors throughout the ZStack platform are properly documented, elaborated, and translated for user-friendly presentation.


🌐 Web query:

ZStack Platform.err() method signature ErrorCode documentation

💡 Result:

I couldn't find a public API doc for a Platform.err() method. ZStack's documented ErrorCode type (used in API responses) has these fields: code (String), description, details, elaboration, opaque (map), and cause (an ErrorCode). See ZStack API docs. [1][2]

If you need the exact Platform.err(...) Java/SDK method signature from source code, tell me which ZStack version and I’ll look up the repository/source for that method.

Sources:

  • ZStack ErrorCode / GetElaborations API docs. [1]
  • ZStack ErrorCode description in API reference (English). [2]

参数顺序错误:err() 方法调用的参数不符合方法签名。

根据 Platform 类文档,err() 方法的正确签名为:

  • err(Enum errCode, ErrorCode cause, String fmt, Object...args)

当前代码的调用为:

err(ORG_ZSTACK_COMPUTE_ZONE_10002, SysErrors.DELETE_RESOURCE_ERROR, errCode, errCode.getDetails())

参数顺序错误。应将枚举类型的 SysErrors.DELETE_RESOURCE_ERROR 作为第一个参数,其他参数依次调整。正确的调用应类似于:

err(SysErrors.DELETE_RESOURCE_ERROR, errCode, ORG_ZSTACK_COMPUTE_ZONE_10002, errCode.getDetails())

或根据实际需求调整参数顺序以匹配方法签名。

🤖 Prompt for AI Agents
In @compute/src/main/java/org/zstack/compute/zone/ZoneBase.java around lines 240
- 246, The err() call in the FlowErrorHandler.handle is using the wrong
parameter order; update the invocation to match the Platform.err(Enum errCode,
ErrorCode cause, String fmt, Object... args) signature by passing
SysErrors.DELETE_RESOURCE_ERROR as the first argument, the current errCode as
the second (cause), then ORG_ZSTACK_COMPUTE_ZONE_10002 as the fmt (or formatted
message key), and errCode.getDetails() as the vararg; modify the call inside the
anonymous FlowErrorHandler (handle method) where evt.setError(err(...)) is
invoked and keep publishing evt via bus.publish(evt).

Comment on lines 1799 to 1803
String details = trash.makeSureInstallPathNotUsed(inv);
if (details != null) {
completion.success(new TrashCleanupResult(inv.getResourceUuid(), inv.getTrashId(), operr(details)));
completion.success(new TrashCleanupResult(inv.getResourceUuid(), inv.getTrashId(), operr(ORG_ZSTACK_STORAGE_CEPH_PRIMARY_10002, details)));
return;
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

避免把外部字符串当作格式化模板:operr(code, details) 可能在 details 含 % 时崩溃

details 来自运行时拼接/外部输入路径,若包含 %String.format 风格实现会抛 MissingFormatArgumentException(或类似异常),导致本来是“不可清理”的场景变成“流程异常”。建议统一改为 operr(code, "%s", details)

建议修复(最小 diff)
- completion.success(new TrashCleanupResult(inv.getResourceUuid(), inv.getTrashId(), operr(ORG_ZSTACK_STORAGE_CEPH_PRIMARY_10002, details)));
+ completion.success(new TrashCleanupResult(inv.getResourceUuid(), inv.getTrashId(), operr(ORG_ZSTACK_STORAGE_CEPH_PRIMARY_10002, "%s", details)));

...

- results.add(new TrashCleanupResult(inv.getResourceUuid(), inv.getTrashId(), operr(ORG_ZSTACK_STORAGE_CEPH_PRIMARY_10003, details)));
+ results.add(new TrashCleanupResult(inv.getResourceUuid(), inv.getTrashId(), operr(ORG_ZSTACK_STORAGE_CEPH_PRIMARY_10003, "%s", details)));

Also applies to: 1894-1899

🤖 Prompt for AI Agents
In
@plugin/ceph/src/main/java/org/zstack/storage/ceph/primary/CephPrimaryStorageBase.java
around lines 1799 - 1803, The call to
operr(ORG_ZSTACK_STORAGE_CEPH_PRIMARY_10002, details) in CephPrimaryStorageBase
(after trash.makeSureInstallPathNotUsed(inv)) treats an external string as a
format template and can throw when details contains '%'—change the call to pass
a format specifier and the value, e.g.
operr(ORG_ZSTACK_STORAGE_CEPH_PRIMARY_10002, "%s", details), and make the same
change for the similar operr usage around the 1894-1899 area; update the
inv.getResourceUuid()/inv.getTrashId() completion.success invocation to use the
new operr call signature.

sender.send(cmd, RESET_DEFAULT_GATEWAY_PATH, wrapper -> {
ResetDefaultGatewayRsp rsp = wrapper.getResponse(ResetDefaultGatewayRsp.class);
return rsp.isSuccess() ? null : operr(rsp.getError(), "operation error, because:%s", rsp.getError());
return rsp.isSuccess() ? null : operr(ORG_ZSTACK_NETWORK_SERVICE_FLAT_10028, rsp.getError(), "operation error, because:%s", rsp.getError());
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

operr() 调用参数错误

此处 operr() 调用与其他位置的用法不一致。对比:

  • 第 1299 行: operr(ORG_ZSTACK_NETWORK_SERVICE_FLAT_10026, "operation error, because:%s", rsp.getError())
  • 第 2825 行: operr(ORG_ZSTACK_NETWORK_SERVICE_FLAT_10054, "operation error, because:%s", rsp.getError())

当前写法多传了一个 rsp.getError() 参数,可能导致错误信息格式化异常。

建议修复
-            return rsp.isSuccess() ? null : operr(ORG_ZSTACK_NETWORK_SERVICE_FLAT_10028, rsp.getError(), "operation error, because:%s", rsp.getError());
+            return rsp.isSuccess() ? null : operr(ORG_ZSTACK_NETWORK_SERVICE_FLAT_10028, "operation error, because:%s", rsp.getError());
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
return rsp.isSuccess() ? null : operr(ORG_ZSTACK_NETWORK_SERVICE_FLAT_10028, rsp.getError(), "operation error, because:%s", rsp.getError());
return rsp.isSuccess() ? null : operr(ORG_ZSTACK_NETWORK_SERVICE_FLAT_10028, "operation error, because:%s", rsp.getError());
🤖 Prompt for AI Agents
In
@plugin/flatNetworkProvider/src/main/java/org/zstack/network/service/flat/FlatDhcpBackend.java
at line 2182, The operr() call in FlatDhcpBackend currently passes
rsp.getError() as an extra first argument
(operr(ORG_ZSTACK_NETWORK_SERVICE_FLAT_10028, rsp.getError(), "operation error,
because:%s", rsp.getError())), which is inconsistent with other usages; change
it to match the pattern used elsewhere by calling
operr(ORG_ZSTACK_NETWORK_SERVICE_FLAT_10028, "operation error, because:%s",
rsp.getError()) so only the format string and the error parameter are supplied
(keep ORG_ZSTACK_NETWORK_SERVICE_FLAT_10028 and rsp.getError() as the format
arg).

Comment on lines 2494 to 2500
if (msg.getDhcpv6ServerIp() != null) {
if (Q.New(UsedIpVO.class).eq(UsedIpVO_.ip, msg.getDhcpv6ServerIp())
.eq(UsedIpVO_.l3NetworkUuid, msg.getL3NetworkUuid()).isExists()) {
throw new ApiMessageInterceptionException(argerr("could set dhcp v6 server ip, because ip[%s] is used",
throw new ApiMessageInterceptionException(argerr(ORG_ZSTACK_NETWORK_SERVICE_FLAT_10051, "could set dhcp v6 server ip, because ip[%s] is used",
msg.getDhcpServerIp()));
}
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

错误信息使用了错误的变量

此代码块验证的是 msg.getDhcpv6ServerIp(),但错误信息中使用的是 msg.getDhcpServerIp()。这会导致用户看到错误的 IP 地址信息。

建议修复
         if (msg.getDhcpv6ServerIp() != null) {
             if (Q.New(UsedIpVO.class).eq(UsedIpVO_.ip, msg.getDhcpv6ServerIp())
                     .eq(UsedIpVO_.l3NetworkUuid, msg.getL3NetworkUuid()).isExists()) {
-                throw new ApiMessageInterceptionException(argerr(ORG_ZSTACK_NETWORK_SERVICE_FLAT_10051, "could set dhcp v6 server ip, because ip[%s] is used",
-                        msg.getDhcpServerIp()));
+                throw new ApiMessageInterceptionException(argerr(ORG_ZSTACK_NETWORK_SERVICE_FLAT_10051, "could not set dhcp v6 server ip, because ip[%s] is used",
+                        msg.getDhcpv6ServerIp()));
             }
         }
🤖 Prompt for AI Agents
In
@plugin/flatNetworkProvider/src/main/java/org/zstack/network/service/flat/FlatDhcpBackend.java
around lines 2494 - 2500, In FlatDhcpBackend.java inside the validation block
that checks msg.getDhcpv6ServerIp(), replace the incorrect error variable
msg.getDhcpServerIp() with msg.getDhcpv6ServerIp() in the
ApiMessageInterceptionException so the thrown error reports the actual IPv6 DHCP
server IP; update the argerr call accordingly where the UsedIpVO check and
exception are raised.

Comment on lines 127 to 131
if(!(LdapConstant.OpenLdap.TYPE.equals(type) || LdapConstant.WindowsAD.TYPE.equals(type))){
throw new ApiMessageInterceptionException(
argerr("Wrong LdapServerType[%s], valid values: [%,%s]", type, LdapConstant.OpenLdap.TYPE, LdapConstant.WindowsAD.TYPE)
argerr(ORG_ZSTACK_LDAP_10010, "Wrong LdapServerType[%s], valid values: [%,%s]", type, LdapConstant.OpenLdap.TYPE, LdapConstant.WindowsAD.TYPE)
);
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

格式化字符串错误:缺少格式说明符 %s

[%,%s] 中的 %, 是无效的格式说明符,应该是 %s,%s。当前代码传入了 3 个参数(type, OpenLdap.TYPE, WindowsAD.TYPE),但格式字符串只有 2 个有效的 %s,会导致运行时输出不正确或抛出异常。

🐛 修复格式化字符串
         if(!(LdapConstant.OpenLdap.TYPE.equals(type) || LdapConstant.WindowsAD.TYPE.equals(type))){
             throw new ApiMessageInterceptionException(
-                    argerr(ORG_ZSTACK_LDAP_10010, "Wrong LdapServerType[%s], valid values: [%,%s]", type, LdapConstant.OpenLdap.TYPE, LdapConstant.WindowsAD.TYPE)
+                    argerr(ORG_ZSTACK_LDAP_10010, "Wrong LdapServerType[%s], valid values: [%s,%s]", type, LdapConstant.OpenLdap.TYPE, LdapConstant.WindowsAD.TYPE)
             );
         }
🤖 Prompt for AI Agents
In @plugin/ldap/src/main/java/org/zstack/ldap/LdapApiInterceptor.java around
lines 127 - 131, The format string in LdapApiInterceptor's
ApiMessageInterceptionException call is wrong ("[%,%s]" has an invalid
specifier) and mismatches the three arguments; update the argerr invocation
(ORG_ZSTACK_LDAP_10010) to use a correct format string such as "Wrong
LdapServerType[%s], valid values: [%s,%s]" so the placeholders align with the
provided values (type, LdapConstant.OpenLdap.TYPE, LdapConstant.WindowsAD.TYPE).

Comment on lines 3798 to 3802
@Override
public void fail(ErrorCode errorCode) {
completion.fail(operr("cannot find flag file [%s] on host [%s], because: %s", makeInitializedFilePath(), hostUuid, errorCode.getCause().getDetails()));
completion.fail(operr(ORG_ZSTACK_STORAGE_PRIMARY_LOCAL_10081, "cannot find flag file [%s] on host [%s], because: %s", makeInitializedFilePath(), hostUuid, errorCode.getCause().getDetails()));
}
});
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

潜在 NPE:errorCode.getCause() 可能为 null

这里拼接 errorCode.getCause().getDetails() 一旦 cause 为空会直接在失败路径再抛 NPE,导致真实错误被覆盖。建议使用 errorCode.getDetails() 兜底,或对 getCause() 做 null-safe。

建议修复(null-safe 获取原因)
@@
         public void fail(ErrorCode errorCode) {
-            completion.fail(operr(ORG_ZSTACK_STORAGE_PRIMARY_LOCAL_10081, "cannot find flag file [%s] on host [%s], because: %s", makeInitializedFilePath(), hostUuid, errorCode.getCause().getDetails()));
+            String reason = errorCode.getCause() != null ? errorCode.getCause().getDetails() : errorCode.getDetails();
+            completion.fail(operr(ORG_ZSTACK_STORAGE_PRIMARY_LOCAL_10081,
+                    "cannot find flag file [%s] on host [%s], because: %s",
+                    makeInitializedFilePath(), hostUuid, reason));
         }
@@
         public void fail(ErrorCode errorCode) {
-            completion.fail(operr(ORG_ZSTACK_STORAGE_PRIMARY_LOCAL_10082, "cannot create flag file [%s] on host [%s], because: %s", makeInitializedFilePath(), hostUuid, errorCode.getCause().getDetails()));
+            String reason = errorCode.getCause() != null ? errorCode.getCause().getDetails() : errorCode.getDetails();
+            completion.fail(operr(ORG_ZSTACK_STORAGE_PRIMARY_LOCAL_10082,
+                    "cannot create flag file [%s] on host [%s], because: %s",
+                    makeInitializedFilePath(), hostUuid, reason));
         }

Also applies to: 3819-3823

🤖 Prompt for AI Agents
In
@plugin/localstorage/src/main/java/org/zstack/storage/primary/local/LocalStorageKvmBackend.java
around lines 3798 - 3802, In the LocalStorageKvmBackend anonymous fail(ErrorCode
errorCode) handler, avoid calling errorCode.getCause().getDetails() directly
because getCause() may be null; instead build the message null-safely (e.g., use
errorCode.getCause() != null ? errorCode.getCause().getDetails() :
errorCode.getDetails()) when calling completion.fail(operr(...)) so the failure
path never throws an NPE; apply the same null-safe pattern to the other similar
fail handlers (the one around makeInitializedFilePath() / hostUuid and the
occurrence noted at the other block).

Comment on lines +1258 to 1260
completion.fail(operr(ORG_ZSTACK_STORAGE_PRIMARY_NFS_10036, "failed to delete bits[%s] on nfs primary storage[uuid:%s], %s, will clean up " +
"installPath, pinv.getUuid(), rsp.getError()",
installPath, pinv.getUuid(), rsp.getError()));
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

错误信息格式存在 Bug:包含了不应出现的字面量文本。

格式字符串中包含了 "installPath, pinv.getUuid(), rsp.getError()" 这段字面量文本,这看起来是复制粘贴错误。实际的格式化参数已经在下一行正确传递了。

🐛 修复建议
-                    completion.fail(operr(ORG_ZSTACK_STORAGE_PRIMARY_NFS_10036, "failed to delete bits[%s] on nfs primary storage[uuid:%s], %s, will clean up " +
-                            "installPath, pinv.getUuid(), rsp.getError()",
+                    completion.fail(operr(ORG_ZSTACK_STORAGE_PRIMARY_NFS_10036, "failed to delete bits[%s] on nfs primary storage[uuid:%s], %s, will clean up",
                             installPath, pinv.getUuid(), rsp.getError()));
🤖 Prompt for AI Agents
In
@plugin/nfsPrimaryStorage/src/main/java/org/zstack/storage/primary/nfs/NfsPrimaryStorageKVMBackend.java
around lines 1258 - 1260, The error message format string in the completion.fail
call for operr(ORG_ZSTACK_STORAGE_PRIMARY_NFS_10036, ...) mistakenly contains
the literal text "installPath, pinv.getUuid(), rsp.getError()"; remove that
literal and use proper format placeholders (e.g., %s) matching the passed
arguments (installPath, pinv.getUuid(), rsp.getError()) so the log shows the
actual values when completion.fail is invoked in NfsPrimaryStorageKVMBackend.

@zstack-robot-2 zstack-robot-2 deleted the sync/lin.ma/5.5.6_global_error_code@@2 branch January 13, 2026 02:02
@MatheMatrix MatheMatrix restored the sync/lin.ma/5.5.6_global_error_code@@2 branch January 13, 2026 02:02
Sign up for free to 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.

3 participants