binarywang/WxJava · error · WxErrorException

X-OPENAI-TOKEN不能为空,请先调用getAccessToken或手动设置

Error message

X-OPENAI-TOKEN不能为空,请先调用getAccessToken或手动设置

What it means

Thrown by `executeDialogPost` in the AI Speech service when a dialog call is made with `withOpenToken=true` but `configStorage.openAiToken` is blank. The OpenAI token must be obtained beforehand via `getAccessToken` or set manually, then sent as the `X-OPENAI-TOKEN` header. It is a WxErrorException.

Source

Thrown at weixin-java-aispeech/src/main/java/me/chanjar/weixin/aispeech/api/impl/WxAispeechServiceImpl.java:106

  protected String executeDialogPost(String path, Object requestBody, boolean withOpenToken, String appid)
    throws WxErrorException {
    String body = toBody(requestBody);
    String requestId = UUID.randomUUID().toString();
    long timestamp = System.currentTimeMillis() / 1000;
    String nonce = randomNonce();
    String sign = WxAispeechSignUtil.calcDialogSign(configStorage.getToken(), timestamp, nonce, body);
    String resolvedAppid = StringUtils.defaultIfBlank(appid, configStorage.getAppid());

    HttpPost request = new HttpPost(configStorage.getDialogApiBaseUrl() + path);
    request.setHeader("request_id", requestId);
    request.setHeader("timestamp", String.valueOf(timestamp));
    request.setHeader("nonce", nonce);
    request.setHeader("sign", sign);
    request.setHeader("Content-Type", ContentType.APPLICATION_JSON.getMimeType());
    if (withOpenToken) {
      if (StringUtils.isBlank(configStorage.getOpenAiToken())) {
        throw new WxErrorException("X-OPENAI-TOKEN不能为空,请先调用getAccessToken或手动设置");
      }
      request.setHeader("X-OPENAI-TOKEN", configStorage.getOpenAiToken());
    } else {
      if (StringUtils.isBlank(resolvedAppid)) {
        throw new WxErrorException("X-APPID不能为空");
      }
      request.setHeader("X-APPID", resolvedAppid);
    }
    request.setEntity(new StringEntity(body, ContentType.APPLICATION_JSON));
    return executeRequest(request);
  }

  protected String executeKnowledgeGet(String path, Map<String, String> queryParams) throws WxErrorException {
    try {
      URIBuilder builder = new URIBuilder(configStorage.getKnowledgeApiBaseUrl() + path);
      if (queryParams != null) {
        for (Map.Entry<String, String> entry : queryParams.entrySet()) {
          if (entry.getValue() != null) {

View on GitHub (pinned to 1c43293a3c)

Solutions

  1. Call `service.getAccessToken(...)` before the dialog call to populate openAiToken.
  2. Or manually set the token on the config storage: `configStorage.setOpenAiToken(token)`.
  3. Refresh the token on its documented expiry cadence.
  4. Verify the credentials used for getAccessToken are correct.

Example fix

// before
String r = service.dialog(...);  // withOpenToken path -> throws
// after
service.getAccessToken(appid, secretKey);  // populates openAiToken
String r = service.dialog(...);
Defensive patterns

Strategy: validation

Validate before calling

// Ensure openAiToken is present before any OpenAI-token dialog call
if (withOpenToken && StringUtils.isBlank(configStorage.getOpenAiToken())) {
    service.getAccessToken(appid, secretKey); // populate token
    if (StringUtils.isBlank(configStorage.getOpenAiToken())) {
        throw new IllegalStateException("Unable to obtain OpenAI token; check credentials");
    }
}

Prevention

When it happens

Trigger: Calling a dialog method that requires the OpenAI token before ever calling `getAccessToken(...)`, or after the token was cleared/expired and never refreshed, with no manual `setOpenAiToken` either.

Common situations: First dialog call after startup; token expired and not refreshed; misconfigured credentials causing getAccessToken to silently leave the field blank.

Related errors


AI-assisted analysis of binarywang/WxJava@1c43293a3c (2026-08-14). Data as JSON: /api/errors/52080e135acd3de2. Report an issue: GitHub.