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

  1. Inspect the wrapped ClientException in the log ('send sms message error') for the Aliyun error code (e.g. SignatureDoesNotMatch, InvalidAccessKeyId).
  2. Verify aliyun accessKeyId/accessKeySecret and SMS region configuration in the environment.
  3. Confirm the SMS template code and sign name are approved and active in the Aliyun console.
  4. Check network egress from the service host to the dysmsapi endpoint (curl the endpoint).
  5. 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

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)