paascloud/paascloud-master · error · OpcBizException
OPC10040004
OPC10040004
Error message
OPC10040004
What it means
OPC10040004 ("发送短信失败") is thrown by OptSmsServiceImpl.sendSms when the Aliyun Dysmsapi call iAcsClient.getAcsResponse(sendSmsRequest) raises a ClientException. This wraps the Aliyun SDK client exception (network to Aliyun endpoint, invalid credentials/signature, throttling) as an OpcBizException after logging. The SMS was not confirmed sent.
Solutions
- Inspect the wrapped ClientException in the log ('send sms message error') for the Aliyun error code (e.g. SignatureDoesNotMatch, InvalidAccessKeyId).
- Verify aliyun accessKeyId/accessKeySecret and SMS region configuration in the environment.
- Confirm the SMS template code and sign name are approved and active in the Aliyun console.
- Check network egress from the service host to the dysmsapi endpoint (curl the endpoint).
- Retry transient network failures with backoff; for persistent credential errors fix the keys, not the retry.
Example fix
// before: unguarded call surfaces raw ClientException
acsResponse = iAcsClient.getAcsResponse(sendSmsRequest);
// after: pre-validate credentials/config before sending
if (StringUtils.isBlank(accessKeyId) || StringUtils.isBlank(accessKeySecret)) {
throw new IllegalStateException("aliyun sms credentials not configured");
}
acsResponse = iAcsClient.getAcsResponse(sendSmsRequest); Defensive patterns
Strategy: retry
Validate before calling
if (StringUtils.isAnyBlank(smsAccessKeyId, smsAccessKeySecret, smsSignName, smsTemplateCode)) {
throw new IllegalStateException("Aliyun SMS configuration incomplete");
}
if (!ValidateUtil.isMobileNumber(phone)) { throw new IllegalArgumentException("bad phone"); } Try / catch
try {
smsService.sendSms(sendSmsRequest);
} catch (OpcBizException e) {
if (e.getCode() == 10040004 && isTransient(e)) {
retryWithBackoff(() -> smsService.sendSms(sendSmsRequest), 3);
} else {
alertOps(e); // credential/config errors are not retryable
throw e;
}
} Prevention
- Rotate and verify Aliyun credentials in a deploy checklist.
- Monitor Aliyun SMS quotas and throttling limits.
- Keep SMS templates/signatures approved before release.
- Test endpoint connectivity from production network (VPC egress).
- Log the Aliyun response code on every failure for fast triage.
When it happens
Trigger: Calling sendSms (outside 'test' profile, which short-circuits) when the Aliyun SMS client throws ClientException: network unreachable, wrong accessKeyId/Secret, invalid signature or region, or SDK request malformed.
Common situations: Aliyun access keys rotated or missing in the environment; VPC/egress firewall blocking the dysmsapi endpoint; expired or unapproved SMS template/signature; exceeding Aliyun SMS rate limits; region endpoint misconfigured.
Understand the failure class
Background: "API request failed": what wrapped HTTP errors from external APIs mean and how to find the real cause — this error's family across 29 libraries.
Related errors
AI-assisted analysis of paascloud/paascloud-master@781281a950 (2026-09-10).
Data as JSON: /api/errors/ce6c6ed32b7a8179.
Report an issue: GitHub.
Appendix: source
Thrown at paascloud-provider/paascloud-provider-opc/src/main/java/com/paascloud/provider/service/impl/OptSmsServiceImpl.java:62
private String profile;
@Override
public SendSmsResponse sendSms(SendSmsRequest sendSmsRequest) {
checkParam(sendSmsRequest);
SendSmsResponse acsResponse;
try {
if (GlobalConstant.DEV_PROFILE.equals(profile)) {
log.error("dev环境不发送短信");
return new SendSmsResponse();
}
if (GlobalConstant.TEST_PROFILE.equals(profile)) {
log.error("test环境不发送短信");
return new SendSmsResponse();
}
acsResponse = iAcsClient.getAcsResponse(sendSmsRequest);
} catch (ClientException e) {
log.error("send sms message error={}", e.getMessage(), e);
throw new OpcBizException(ErrorCodeEnum.OPC10040004, e);
}
log.info("send sms message OK acsResponse={}", acsResponse);
return acsResponse;
}
private void checkParam(SendSmsRequest sendSmsRequest) {
String templateCode = sendSmsRequest.getTemplateCode();
String signName = sendSmsRequest.getSignName();
if (StringUtils.isBlank(signName)) {
sendSmsRequest.setSignName(paascloudProperties.getAliyun().getSms().getSignName());
}
String templateParam = sendSmsRequest.getTemplateParam();
String phoneNumbers = sendSmsRequest.getPhoneNumbers();
Preconditions.checkArgument(StringUtils.isNotEmpty(templateCode), "短信模板不能为空");
Preconditions.checkArgument(StringUtils.isNotEmpty(phoneNumbers), "手机号码不能为空");
Preconditions.checkArgument(StringUtils.isNotEmpty(templateParam), "短信内容不能为空");View on GitHub (pinned to 781281a950)