适用版本: 6.8-7.15
1. 错误异常的基本描述 #
invalid time hour value [n] (possible values may be between 0 and 23 incl.) 是 Elasticsearch 在解析时间表达式(常见于 Watcher 的 trigger 调度配置)时抛出的异常。该错误表示 hour 字段已被成功解析为数字,但数值不在 0 到 23 的合法范围内。
常见现象 #
- 创建或更新 Watcher 时返回
400 Bad Request,响应体中包含上述错误信息。 - Watcher 无法注册,
_watcher/stats接口显示对应 watch 处于failed状态。 - Elasticsearch 日志中出现类似如下记录:
ElasticsearchParseException: invalid time hour value [24] (possible values may be between 0 and 23 incl.)
at org.elasticsearch.xpack.watcher.trigger.schedule.DayTimes.validHour(DayTimes.java)
at org.elasticsearch.xpack.watcher.trigger.schedule.CronSchedule.parse(CronSchedule.java)
典型报错与异常栈 #
以下为实际场景中可能遇到的报错形态:
{
"error": {
"root_cause": [
{
"type": "parse_exception",
"reason": "invalid time hour value [24] (possible values may be between 0 and 23 incl.)"
}
],
"type": "parse_exception",
"reason": "invalid time hour value [24] (possible values may be between 0 and 23 incl.)"
},
"status": 400
}
2. 为什么会发生这个错误 #
Elasticsearch 的 Watcher 调度模块在解析 hour 字段时,会先读取数值,再调用 DayTimes.validHour(hour) 进行范围校验。只要数值不在 [0, 23] 闭区间内,就会立即抛出 ElasticsearchParseException。
常见原因通常包括:
- 使用了 24 小时制边界值错误:如将午夜写成
24而不是0,24:00不是合法的 24 小时制表示。 - cron 表达式配置错误:cron 的 hour 字段误写为
24-23或24等越界值。 - 时区转换引入偏移:UTC 时间转换为本地时间时,偏移量计算错误导致 hour 落在范围外。
- 动态计算 hour 值未做边界校验:通过脚本或模板动态生成 hour 值时,未对结果做
0-23的截断或取模处理。 - 数据来源污染:从外部系统读取的时间字符串包含非法值(如
99、-1),解析后直接传入调度配置。 - 版本差异:不同 Elasticsearch 版本对时间表达式的解析严格程度不同,升级后原本"宽松"的配置可能失效。
3. 如何排查和解决这个异常 #
建议按"先定位报错来源,再修正配置,后验证生效"的顺序处理:
- 定位报错中的具体 hour 值:从异常信息中提取方括号中的数值(如
[24]),确认越界的具体数字。 - 检查 Watcher 的 trigger 配置:调用
GET _watcher/watch/<watch_id>查看对应 watch 的trigger.schedule部分,重点检查hour字段。 - 检查 cron 表达式:如果使用
cron类型的 schedule,确认 cron 表达式中 hour 字段的值是否合法。 - 排查 hour 值的来源:如果 hour 值来自动态模板、脚本计算或外部数据源,追溯生成逻辑。
- 确认时区设置:检查
trigger.schedule中是否配置了tz(时区),时区偏移是否导致 hour 越界。 - 在测试环境复现:修复后先在测试环境注册同名 watch,确认不再报错再应用到生产环境。
排查时需要注意的问题 #
- 不要只看异常信息的字面含义,必须结合 Watcher 的完整配置(
trigger、condition、actions)一起分析。 - 如果 hour 值来自脚本字段或动态计算,需要同时检查脚本逻辑和输入数据的边界情况。
- 涉及时区转换的场景,建议统一使用 UTC 时间并在展示层做转换,避免调度层出现偏移问题。
- 批量更新 Watcher 时,建议逐个验证,避免将同一个错误配置扩散到多个 watch。
4. 如何解决这个错误 #
常用修复思路 #
- 将 hour 值修正到 0-23 范围内:
24改为0,-1改为23,或根据业务逻辑做正确的偏移计算。 - 不要将
24:00直接拆成hour=24:24:00在 Elasticsearch 时间解析中应表示为第二天的00:00,hour 字段应写0并调整日期。 - 对 cron 表达式做合法性校验:cron 的 hour 字段合法值为
0-23,多个值用逗号分隔,范围用连字符,注意不要写成24-23这种反向区间。 - 在动态生成 hour 值的代码中增加边界检查:使用取模运算确保结果落在合法范围内,例如
hour = ((rawHour % 24) + 24) % 24。 - 统一时区处理策略:明确 Watcher 使用的时区,避免 UTC 与本地时间混用导致的偏移错误。
修复示例 #
错误配置示例(hour = 24):
{
"trigger": {
"schedule": {
"daily": {
"at": {
"hour": [24],
"minute": [0]
}
}
}
}
}
修复后配置示例(hour = 0):
{
"trigger": {
"schedule": {
"daily": {
"at": {
"hour": [0],
"minute": [0]
}
}
}
}
}
cron 表达式错误示例:
{
"trigger": {
"schedule": {
"cron": {
"expression": "0 24 * * *"
}
}
}
}
修复后 cron 表达式(使用 0 表示午夜):
{
"trigger": {
"schedule": {
"cron": {
"expression": "0 0 * * *"
}
}
}
}
带时区配置的修复示例:
{
"trigger": {
"schedule": {
"cron": {
"expression": "0 9 * * *",
"tz": "Asia/Shanghai"
}
}
}
}
后续注意事项与推荐建议 #
- 在创建 Watcher 的代码中增加 hour 值的前置校验,拒绝
0-23以外的值,Fail Fast 优于运行时报错。 - 对动态生成调度配置的场景,建议编写单元测试覆盖边界值(
-1、0、23、24、99等)。 - 建立 Watcher 配置的代码审查机制,重点关注
trigger.schedule部分的 hour、minute、cron 表达式是否合法。 - 定期调用
GET _watcher/stats检查 watch 的执行状态,及时发现因配置错误导致的failed状态。
借助 INFINI 产品提升排障效率 #
- INFINI Console 适合查看集群健康度、索引状态、Watcher 执行历史和错误趋势,帮助快速判断异常是配置问题还是系统性问题。
- INFINI Gateway 适合部署在 Elasticsearch 前面做请求观测、限流和流量治理,可以在 Watcher 触发动作(如 webhook、index 操作)时提供请求审计和异常重试能力。
- 建议将 Watcher 的变更记录、执行日志和错误告警统一接入监控面板,缩短从"发现 watch 执行失败"到"定位配置错误"的时间。
5. 小结 #
invalid time hour value [n] (possible values may be between 0 and 23 incl.) 是一个配置层面的解析异常,本质原因是 hour 数值越界。处理这类异常时,最有效的路径是:从报错信息提取越界值 -> 定位对应 Watcher 的 trigger 配置 -> 修正 hour 值并验证时区和 cron 表达式 -> 在代码中增加边界校验防止复发。
只要把 Watcher 配置的校验规则、时区处理策略和监控手段固定下来,这类异常基本可以在开发阶段就被拦截,不会流入生产环境。
相关错误 #
附:日志上下文 #
下面保留当前页面中的源码片段,便于结合异常调用栈定位问题:
int hour = parser.intValue();
if (DayTimes.validHour(hour) == false) {
throw new ElasticsearchParseException(
"invalid time hour value [{}] (possible values may be between 0 and 23 incl.)",
hour);
}
DayTimes.validHour() 的校验逻辑等价于:
public static boolean validHour(int hour) {
return hour >= 0 && hour <= 23;
}





