paascloud/paascloud-master · error · OpcBizException

OPC10040006

OPC10040006

Error message

OPC10040006

What it means

OPC10040006 ("获取模板信息失败") is thrown by OptSendMailServiceImpl.sendTemplateMail when the Velocity template rendering (optVelocityService.getTemplate) fails with IOException or TemplateException. IOException usually means the template resource cannot be found/read; TemplateException means the template parsed/rendered badly (bad syntax, missing variable directives). The exception wraps the cause and the mail is never sent.

Solutions

  1. Verify templateLocation resolves on the classpath (e.g. starts with a correct classpath prefix and the file exists in the built artifact).
  2. Check the wrapped IOException/TemplateException in the log — TemplateException gives line/column of the syntax error.
  3. Fix Velocity template syntax errors (#if/#foreach directives, $variable references) reported in the cause.
  4. Ensure the model map contains every variable the template references.
  5. Add a startup check/test that renders each registered template to catch missing templates at deploy time.

Example fix

// before: template path typo fails at runtime
mailService.sendTemplateMail(model, "/templates/mail/notif.vm", subject, to);
// after: fail fast if template missing
classPathResource "/templates/mail/notify.vm";
try (InputStream in = getClass().getResourceAsStream(templateLocation)) {
    if (in == null) { throw new IllegalStateException("template missing: " + templateLocation); }
}
mailService.sendTemplateMail(model, "/templates/mail/notify.vm", subject, to);
Defensive patterns

Strategy: validation

Validate before calling

try (InputStream in = getClass().getResourceAsStream(templateLocation)) {
    if (in == null) {
        throw new IllegalStateException("Mail template not on classpath: " + templateLocation);
    }
}

Type guard

boolean templateExists(String location) {
    return getClass().getResource(location) != null;
}

Try / catch

try {
    mailService.sendTemplateMail(model, templateLocation, subject, to);
} catch (OpcBizException e) {
    if (e.getCode() == 10040006) {
        log.error("Mail template render failed for {}", templateLocation, e.getCause());
    }
    throw e;
}

Prevention

When it happens

Trigger: Calling sendTemplateMail(model, templateLocation, subject, to) where templateLocation does not resolve to an existing template resource, or the .vm template has Velocity syntax errors, or the model lacks variables the template requires.

Common situations: Renaming/moving template files without updating templateLocation; packaging templates outside the jar so classpath loading fails; Velocity directive typos after editing; template referencing a model key removed in a new release.

Understand the failure class

Background: "File not found" and ENOENT errors: why libraries can't find a file that should exist — this error's family across 50 libraries.

Related errors


AI-assisted analysis of paascloud/paascloud-master@781281a950 (2026-09-10). Data as JSON: /api/errors/bee7cf21921981ad. Report an issue: GitHub.

Appendix: source

Thrown at paascloud-provider/paascloud-provider-opc/src/main/java/com/paascloud/provider/service/impl/OptSendMailServiceImpl.java:116

			helper.setSubject(subject);
			helper.setText(text, true);
		} catch (MessagingException e) {
			log.error("生成邮件消息体, 出现异常={}", e.getMessage(), e);
			throw new OpcBizException(ErrorCodeEnum.OPC10040005);
		}
		return mimeMessage;
	}

	@Override
	public int sendTemplateMail(Map<String, Object> model, String templateLocation, String subject, Set<String> to) {
		log.info("sendTemplateMail - 发送模板邮件. subject={}, model={}, to={}, templateLocation={}", subject, model, to, templateLocation);

		String text;
		try {
			text = optVelocityService.getTemplate(model, templateLocation);
		} catch (IOException | TemplateException e) {
			log.info("sendTemplateMail [FAIL] ex={}", e.getMessage(), e);
			throw new OpcBizException(ErrorCodeEnum.OPC10040006, e);
		}
		return this.sendTemplateMail(subject, text, to);
	}


	private String[] setToArray(Set<String> to) {
		Preconditions.checkArgument(PublicUtil.isNotEmpty(to), "请输入收件人邮箱");

		Set<String> toSet = Sets.newHashSet();
		for (String toStr : to) {
			toStr = toStr.trim();
			if (PubUtils.isEmail(toStr)) {
				toSet.add(toStr);
			}
		}
		if (PublicUtil.isEmpty(toSet)) {
			return null;
		}

View on GitHub (pinned to 781281a950)