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

Open
#6,786 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-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

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 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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.