适用版本: 7.x-8.x
1. 错误异常的基本描述 #
failed to parse, document is empty 表示 Elasticsearch 在解析待索引文档时,发现请求里确实传了 source,但它的长度为 0,也就是空文档。从日志上下文可以看到,源码在真正创建 XContentParser 之前就先检查:
source.source() != null- 且
source.source().length() == 0
满足这两个条件就直接抛 DocumentParsingException。这不是字段映射冲突,而是请求体本身没有有效内容。
常见现象 #
- 写入文档或批量写入(Bulk)时返回
400 Bad Request,提示文档为空。 - 批量写入时某一行文档源内容丢失,导致整个 Bulk 请求部分失败。
- 预处理或过滤逻辑把字段全部删空了,但仍继续发送请求。
- 网关或序列化层错误地清空了请求体。
- Elasticsearch 日志中可以看到
failed to parse, document is empty关键字,伴随DocumentParsingException。 - 问题通常出现在特定文档或批量请求中的特定行,而不是所有写入。
典型报错与异常栈 #
常见日志形态通常类似下面这样:
DocumentParsingException: failed to parse, document is empty
at org.elasticsearch.index.mapper.DocumentParserContext...
或者批量写入时:
DocumentParsingException[failed to parse, document is empty]
at org.elasticsearch.index.mapper.DocumentParserContext...
2. 为什么会发生这个错误 #
failed to parse, document is empty 的根因是"写入请求的 source 内容为空"。Elasticsearch 要求写入的文档至少包含一个有效的内容(即使是空对象 {} 也可以),如果 source 长度为 0(空字符串或空字节数组),就会抛出此异常。
常见原因通常包括:
- 应用层发送了空内容:应用层把空字符串或空字节数组发给了索引接口。
- 批量写入时文档源内容丢失:批量写入(Bulk)时某一行文档的 source 为空。
- 预处理逻辑清空了内容:预处理或过滤逻辑把字段全部删空了,但仍继续发送请求。
- 网关或序列化层错误:网关或序列化层错误地清空了请求体。
- 模板渲染后内容为空:如果使用模板生成文档内容,渲染后可能得到空内容。
- 字段过滤过度:应用层在构建文档时过滤掉了所有字段,得到空文档。
- 批量导入工具问题:使用批量导入工具时,源数据文件中存在空行或格式错误。
3. 如何排查和解决这个异常和解决这个异常 #
建议按"先检查请求体、再定位空内容来源、后修复生成逻辑"的顺序处理:
打印发送前的
_source原文长度:重点检查写入链路而不是 mapping。# 查看 Elasticsearch 日志中的具体错误信息 grep -r "failed to parse, document is empty" /var/log/elasticsearch/如果使用 Bulk,定位具体哪一条 action 对应的 source 为空:
# 查看 Bulk 请求内容(如果记录了) # Bulk 格式是 action 和 source 交替的行 # 例如:{"index": {"_id": "1"}} # {"field": "value"} <- 这一行就是 source # 如果 source 行为空,就会触发此错误检查过滤、脱敏、模板渲染或序列化步骤是否把内容清空:
// 在发送前检查 source 长度 if (source == null || source.length() == 0) { throw new IllegalArgumentException("Document source is empty"); }区分"请求体缺失"和"空对象
{}":这个异常对应的是长度为 0,不是空 JSON 对象。// 错误:长度为 0(空字符串) "" // 错误:null null // 正确:空对象(长度为 2,但内容为空) {} // 正确:有内容的对象 {"field": "value"}
排查时需要注意的问题: #
- 这个错误是请求体问题,不是 mapping 或字段类型问题,需要重点关注 source 内容,而不是索引定义。
- 注意区分长度为 0 和空对象
{},后者是合法的(虽然没什么用),前者才触发此错误。 - 如果问题出现在 Bulk 请求中,需要定位具体是哪一行文档的 source 为空。
4. 如何解决这个错误 #
常用修复思路 #
在业务侧增加非空校验:在业务侧增加非空校验,空内容直接拦截。
// 在发送前检查 if (source == null || source.length() == 0) { logger.warn("Skipping empty document: {}", docId); return; // 直接跳过,不发送请求 }批量导入前对每条文档做长度检查:
// 处理 Bulk 请求时 for (Document doc : documents) { if (doc.getSource() == null || doc.getSource().length() == 0) { logger.error("Empty document found: {}", doc.getId()); // 跳过或记录错误 } }如果允许写入空对象,确保实际发送的是
{}:而不是空字符串。// 错误:发送空字符串 "" // 正确:发送空对象 {}对日志采样:记录失败请求体长度和文档 ID,便于快速定位。
// 在日志中记录失败的文档信息 logger.error("Failed to index document: id={}, source_length={}", docId, source.length());修复预处理逻辑:检查过滤、脱敏或模板渲染步骤,确保不会清空文档内容。
// 在预处理后检查 if (doc.getFields().isEmpty()) { logger.warn("All fields filtered out for document: {}", docId); // 决定是跳过还是补充默认字段 }
后续注意事项与推荐建议 #
- 在应用层对写入请求进行校验,确保 source 不为空且长度大于 0。
- 在 CI/CD 流程中加入写入请求校验步骤,在发送前验证其合法性。
- 如果使用 Bulk API,在构建 Bulk 请求时检查每条文档的 source 是否为空。
- 为写入失败配置专门的监控和告警,在空文档错误时及时通知。
- 在批量导入数据时,预先检查源数据文件,避免空行或格式错误。
借助 INFINI 产品提升排障效率 #
- INFINI Console 适合查看集群的索引状态、写入日志、错误趋势和文档内容,帮助快速定位
failed to parse, document is empty是请求问题、预处理问题还是 Bulk 请求问题,并提供可视化的索引管理和写入监控功能。 - INFINI Gateway 可以记录所有写入请求的详细日志和响应内容,帮助定位空文档的具体来源,同时提供请求审计功能,记录哪些文档写入失败。
- 建议将文档写入成功率、空文档错误和 Bulk 请求状态统一接入监控面板,结合 INFINI Console 的告警功能,在空文档错误时及时通知管理员。
5. 小结 #
这是非常明确的输入问题。Elasticsearch 已经告诉你文档主体为空,修复点就在请求生成链路。大多数情况下,这个问题可以通过在业务侧增加非空校验、检查预处理逻辑和验证 Bulk 请求来解决。
只要把请求校验、预处理逻辑和写入监控固定下来,大多数空文档类异常都可以被提前拦截,也更容易通过 INFINI Console 和 INFINI Gateway 实现持续防护。
相关错误 #
- failed-to-parse-mapping-how-to-solve-this-elasticsearch-exception
- failed-to-parse-content-to-map-how-to-solve-this-elasticsearch-exception
- document-parse-exception-how-to-solve-this-elasticsearch-exception
- mapper-parsing-exception-how-to-solve-this-elasticsearch-exception
- invalid-source-discovery-how-to-solve-this-elasticsearch-exception
参考文档 #
- Elasticsearch Index API 官方文档
- Elasticsearch Bulk API 官方文档
- Elasticsearch Document API 官方文档
- INFINI Console 文档
- INFINI Gateway 文档
附:日志上下文 #
下面保留当前页面中的源码或日志片段,便于继续结合异常调用栈定位问题:
* @return the parsed document
* @throws DocumentParsingException whenever there's a problem parsing the document
*/
public ParsedDocument parseDocument(SourceToParse source, MappingLookup mappingLookup) throws DocumentParsingException {
if (source.source() != null && source.source().length() == 0) {
throw new DocumentParsingException(new XContentLocation(0, 0), "failed to parse, document is empty");
}
final RootDocumentParserContext context;
final XContentType xContentType = source.getXContentType();
try (XContentParser parser = XContentHelper.createParser(parserConfiguration, source.source(), xContentType)) {
context = new RootDocumentParserContext(mappingLookup, mappingParserContext, source, parser);





