quickwit-oss / quickwit-oss/quickwit

An empty OTLP export is answered 500

Open
#6,784 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
Rust
Stars
11.7k
Forks
597
Avg merge
2d 22h
Merged PRs (30d)
37

Description

Repository: quickwit-oss/quickwit
Version: 0.8.2 (quickwit/quickwit:0.8.2), QW_ENABLE_OTLP_ENDPOINT=true
Reproduced first-hand: yes, on 2026-09-08.

What happens

An export carrying no records — an ExportLogsServiceRequest with an empty
resource_logs, which encodes to zero protobuf bytes — is answered:

HTTP/1.1 500 Internal Server Error
{"message": "error when ingesting payload: status: Internal, message: \"\", details: [], metadata: MetadataMap { headers: {} }"}

The inner message is empty, so there is nothing for an operator to act on.

Reproduction

docker run -d --name qw -p 7280:7280 \
  -e QW_ENABLE_OTLP_ENDPOINT=true quickwit/quickwit:0.8.2 run
until curl -sf localhost:7280/health/livez >/dev/null; do sleep 1; done; sleep 10

curl -s -X POST localhost:7280/api/v1/otlp/v1/logs \
  -H 'Content-Type: application/x-protobuf' --data-binary '' -w '\nHTTP %{http_code}\n'

Note the sleep 10. Quickwit creates otel-logs-v0_7 shortly after
/health/livez begins answering, and an ingest before that answers 500
index otel-logs-v0_7 not found — a different error, and not this one. The
behaviour above is on a warm node.

Why this is worth reporting

resource_logs is a repeated field and may be empty. Collectors flush on a
timer, so an export with nothing in it is ordinary traffic rather than a client
error: the OpenTelemetry Collector's OTLP exporter will send one whenever a
flush interval elapses with no data.

A 5xx tells the client the failure is the server's and that the request should
be retried, so a collector with nothing to send retries an empty export
indefinitely, and every one of those is an error in its logs.

Four other stores were checked with the same export and all four answer 200.

If Quickwit intends to refuse empty exports, a 4xx with a message would at least
stop the retry loop; accepting them as a no-op would match what the other
implementations do.

Where this came from

SpecMatrix, a conformance corpus for observability backends. The check is
cases/otlp-logs/empty-batch.yaml.


Found by SpecMatrix, a conformance corpus for observability backends. Happy to be told this is configuration or already known — the check will record whichever it turns out to be.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start at the OTLP logs endpoint /api/v1/otlp/v1/logs and reproduce the warm-node case with the provided curl command. Use cases/otlp-logs/empty-batch.yaml as the expected-behavior reference, then trace the request through ingestion. Done means an empty ExportLogsServiceRequest no longer produces an unhelpful 500, with regression coverage for the chosen response.

Written by the indexing model from the issue text.

Assessment

Tech stack
rust
Domain
api, observability
Issue type
Bug
Difficulty
3/5
Estimated time
1-2 days
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
68/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.