📣 极限科技诚招搜索运维工程师(Elasticsearch/Easysearch)- 全职/北京 👉 : 立即申请加入

适用版本: 6.8-8.9

1. 错误异常的基本描述 #

could not parse http request. unexpected token [token] 是 Elasticsearch Watcher 组件在解析 HTTP request 定义时抛出的异常。当 Watcher 的 JSON 解析器在遍历请求定义的过程中,读到了当前位置不应该出现的 token 类型时,就会触发此错误。

常见现象 #

  • 创建或更新 Watcher 时返回 400 Bad Request,响应体中包含 could not parse http request. unexpected token 错误信息。
  • Kibana 或 Elasticsearch API 在保存 Watcher 配置时失败,提示解析错误。
  • 异常信息中会明确指出具体是哪个 token 导致了问题,例如 unexpected token [START_OBJECT]unexpected token [VALUE_NUMBER] 等。
  • 如果 Watcher 是通过脚本动态生成的,可能导致部分 Watcher 正常、部分失败的不一致现象。

典型报错与异常栈 #

常见的错误日志形态如下:

ElasticsearchParseException: could not parse http request. unexpected token [START_OBJECT]
Caused by: com.fasterxml.jackson.core.JsonParseException
at org.elasticsearch.xpack.watcher.actions.web.WebhookActionFactory.parseRequest(...)

或:

{
  "error": {
    "root_cause": [
      {
        "type": "parse_exception",
        "reason": "could not parse http request. unexpected token [VALUE_STRING]"
      }
    ],
    "type": "parse_exception",
    "reason": "could not parse http request. unexpected token [VALUE_STRING]"
  },
  "status": 400
}

2. 为什么会发生这个错误 #

could not parse http request. unexpected token 本质上是 Watcher HTTP request JSON 结构的语法错误。Watcher 的解析器对 HTTP request 定义中的各个字段有严格的格式要求,包括字段类型、嵌套层级和字段顺序等。当 JSON 中的某个 token 不符合解析器在当前位置对 token 类型的预期时,就会抛出此异常。

常见原因 #

  • 字段类型错误:将应该是字符串的字段写成了对象或数组,或将应该是对象的字段写成了字符串。例如 headers 字段应该是对象类型,却写成了字符串。
  • 字段位置错误:将某个字段放在了错误的嵌套层级中。例如将 headers 放在了 body 内部,或将 auth 的配置写在了 request 对象的外层。
  • 多余或缺失的逗号:JSON 语法错误导致解析器读到了意外的 token。
  • 不支持的字段:在 HTTP request 定义中使用了 Watcher 解析器不认识的字段名,或者字段名拼写错误。
  • 数组与对象混用:例如 headers 应该是 {"key": "value"} 格式的对象,却写成了 [{"key": "value"}] 数组格式。
  • 动态脚本生成 JSON 时的类型问题:当使用 Painless 脚本或其他方式动态构造 HTTP request 定义时,变量类型不正确可能导致生成的 JSON 结构异常。

解析器源码逻辑说明 #

从源码来看,Watcher HTTP request 解析器采用递归下降的方式解析 JSON。解析器在进入 parseRequest 方法后,会依次读取 JSON 的各个字段:

  1. 首先读取 { 作为对象开始。
  2. 然后循环读取字段名(字符串 token),根据字段名进入不同的解析分支。
  3. 每个分支对后续 token 的类型有明确要求,例如 scheme 字段期望读取一个字符串 token,port 期望读取一个数字 token,headers 期望读取一个对象 token。
  4. 如果当前 token 类型与任何分支的预期都不匹配,解析器就会抛出 ElasticsearchParseException,并附带当前 token 的类型信息。

3. 如何排查和解决这个异常 #

建议按以下步骤进行排查:

  1. 获取完整错误信息:从 API 响应或 Elasticsearch 日志中获取完整的异常信息,特别注意 unexpected token [XXX] 中的 token 类型提示,这能直接指出问题所在的位置。
  2. 检查 JSON 结构:将 Watcher 的 HTTP request 定义单独取出,使用 JSON 格式化工具进行格式化,检查缩进和层级是否正确。
  3. 对照官方文档:参考 Elasticsearch 官方 Watcher HTTP request 文档,确认各字段的类型和位置是否正确。
  4. 最小化复现:先使用一个最简单的合法 HTTP request 配置,确认可以正常创建 Watcher,然后逐步添加字段,直到找到导致问题的具体字段。
  5. 检查动态内容:如果 request 定义中包含脚本生成的动态内容,检查脚本的输出是否符合预期。

排查时需要注意的问题 #

  • 不要只看 unexpected token 的字面含义,token 类型(如 START_OBJECTVALUE_STRINGVALUE_NUMBER)是定位问题的关键线索。
  • 如果错误发生在 headersauthbody 等复杂字段上,优先检查这些字段的内部结构是否完整。
  • Watcher 的 JSON 定义通常嵌套在 actions 数组内,注意检查外层结构是否正确,避免把外层的错误误判为 HTTP request 内部的错误。
  • 当使用 Kibana Dev Tools 或 curl 提交 Watcher 定义时,注意 shell 对引号和转义字符的处理,避免提交的内容与预期不符。

4. 如何解决这个错误 #

常用修复思路 #

修复字段类型错误 #

headers 字段应该是对象类型,键值对形式:

{
  "actions": {
    "my_webhook": {
      "webhook": {
        "method": "POST",
        "host": "example.com",
        "port": 80,
        "path": "/api/alert",
        "headers": {
          "Content-Type": "application/json",
          "Authorization": "Bearer my-token"
        }
      }
    }
  }
}

错误写法示例(headers 写成了数组):

{
  "headers": [
    {"Content-Type": "application/json"}
  ]
}

修复字段位置错误 #

auth 配置应该放在 webhook 对象内,与 urlhost/port/path 同级:

{
  "actions": {
    "my_webhook": {
      "webhook": {
        "url": "https://example.com/api/alert",
        "auth": {
          "basic": {
            "username": "user",
            "password": "pass"
          }
        }
      }
    }
  }
}

修复 body 格式问题 #

body 字段可以是字符串或对象。如果是 JSON 对象,确保内容正确嵌套:

{
  "actions": {
    "my_webhook": {
      "webhook": {
        "url": "https://example.com/api/alert",
        "body": {
          "alert_name": "{{ctx.watch_id}}",
          "trigger_time": "{{ctx.trigger.triggered_time}}",
          "status": "{{ctx.payload.status}}"
        }
      }
    }
  }
}

使用正确的端口类型 #

port 字段必须是数字,不能是字符串:

{
  "webhook": {
    "host": "example.com",
    "port": 8080,
    "path": "/api/alert"
  }
}

错误写法:"port": "8080"

后续注意事项与推荐建议 #

  • 在开发 Watcher 配置时,先使用 JSON 校验工具(如 jq 或在线 JSON 格式化工具)验证 JSON 的合法性,再提交到 Elasticsearch。
  • 对于复杂的 Watcher 配置,建议先在本地用最小化配置测试通过,再逐步添加条件、变换和动作,每次添加后都验证是否仍然有效。
  • 如果 Watcher 配置是通过 CI/CD 或脚本自动生成的,在生成后增加 JSON 校验步骤,避免将格式错误的配置推送到生产环境。
  • 为 Watcher 配置建立代码审查机制,重点关注 JSON 结构、字段类型和嵌套层级,减少人为失误。

借助 INFINI 产品提升排障效率 #

  • INFINI Console 适合查看集群中 Watcher 的执行状态、失败记录和错误详情,帮助快速定位是哪个 Watcher 的哪一部分配置出了问题。
  • INFINI Gateway 适合部署在 Elasticsearch 前面做请求观测和流量治理,可以捕获 Watcher 触发的 Webhook 请求,便于验证 Webhook 的目标服务是否正常响应。
  • 建议将 Watcher 的执行日志和失败记录统一接入监控面板,设置告警规则,在 Watcher 执行失败时及时通知相关人员。

5. 小结 #

could not parse http request. unexpected token 本质上是 Watcher HTTP request 定义的 JSON 结构语法错误。解决此问题的关键是理解异常信息中的 token 类型提示,对照官方文档检查字段类型、嵌套层级和字段位置。大多数情况下,通过格式化 JSON、最小化复现和逐步排查,都能快速定位并修复问题。

建议在 Watcher 配置的开发和部署流程中引入 JSON 校验和代码审查,从源头减少此类问题的发生。

相关错误 #

附:日志上下文 #

下面保留当前页面中的源码或日志片段,便于继续结合异常调用栈定位问题:

} else {
    throw new ElasticsearchParseException("could not parse http request. unexpected token [{}]", token);
}
if (builder.host == null) {