quickwit-oss / quickwit-oss/quickwit
OTLP record with `time_unix_nano: 0` is rejected with an empty `error_message`, and the response is JSON for a protobuf request
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-09.
What happens
A log record whose time_unix_nano is 0 is answered HTTP 200 with
{ "partial_success": { "rejected_log_records": 1, "error_message": "" } }
and never becomes queryable. Two things about that response are worth
separating.
The rejection is reported, but with no reason. rejected_log_records: 1 is
the right channel and Quickwit uses it, which several stores do not. But
error_message is empty, so a caller learns that one record was dropped and
nothing about why. observed_time_unix_nano is present and carries a real
instant, so there is somewhere to file the record if Quickwit wanted one.
The response is JSON although the request was protobuf. The OTLP/HTTP
specification requires the response to use the same encoding as the request.
Here the body is JSON and content-type says application/json, so a
conformant protobuf client cannot decode it — meaning the report above does not
reach the client it is meant for. (OpenObserve makes the same mistake in the
other direction, answering a JSON export with protobuf; that is filed with them
separately. It is mentioned only so this is not read as singling one project
out.)
Why the record is conformant
opentelemetry-proto documents time_unix_nano as the time the event occurred,
with a value of 0 indicating that the timestamp is unknown or missing. It is not
a malformed field — it is the defined way to say "I do not know when this
happened", and an exporter with no event time emits exactly this.
Refusing it is a defensible choice. Refusing it without saying why is the part
worth fixing, and four other stores accept the record.
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
# One record: severityText INFO, a body, attribute specmatrix.run=issue-tzero,
# observed_time_unix_nano set, and time_unix_nano = 0.
base64 -d > /tmp/tzero.pb <<'B64'
CoEBCh4KHAoMc2VydmljZS5uYW1lEgwKCnNwZWNtYXRyaXgSXwoMCgpzcGVjbWF0cml4Ek8QCRoESU5GTyobChlzcGVjbWF0cml4IHplcm8gdGltZXN0YW1wMh8KDnNwZWNtYXRyaXgucnVuEg0KC2lzc3VlLXR6ZXJvWQB2wF/KcdMY
B64
curl -s -D - -X POST localhost:7280/api/v1/otlp/v1/logs \
-H 'Content-Type: application/x-protobuf' --data-binary @/tmp/tzero.pb
sleep 10
curl -s 'localhost:7280/api/v1/otel-logs-v0_7/search?query=attributes.specmatrix.run:issue-tzero'
Observed: HTTP 200, content-type: application/json, the partial_success body
quoted above, and num_hits: 0.
Where this came from
SpecMatrix, a conformance corpus for observability backends. The check is
cases/otlp-logs/timestamp-zero.yaml, which records rather than judges what a
store does with an unknown timestamp — Parseable files it at the epoch, three
others substitute their ingest time. It became a finding here because the
record is discarded and the reason is not given.
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
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- 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 behavior with the provided protobuf request, then compare it with cases/otlp-logs/timestamp-zero.yaml. Done means the handling of time_unix_nano: 0 reports a useful rejection reason and the response encoding and content type match the request encoding.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- rust
- Domain
- api, backend
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 55/100