Skip to content

Add a JSON schema for the /v1.0/traces endpoint (ETP/v1) - #7448

Draft
darccio wants to merge 4 commits into
mainfrom
dario.castane/tender-bassi-343ee0
Draft

Add a JSON schema for the /v1.0/traces endpoint (ETP/v1)#7448
darccio wants to merge 4 commits into
mainfrom
dario.castane/tender-bassi-343ee0

Conversation

@darccio

@darccio darccio commented Aug 4, 2026

Copy link
Copy Markdown
Member

Motivation

/v1.0/traces (ETP/v1, "efficient trace payload") had no schema file under
tests/schemas/utils/library/, which had v0.1, v0.4, v0.5, v0.6 and v0.7 but no v1.0.

SchemaValidator.get_errors derives the schema id from the request path and returns no error at
all when that file is missing:

schema_id = f"/{self.interface}{path}-request.json"
if schema_id not in _get_schemas_store():
    logger.info(f"Schema {schema_id} does not exists")
    return []

So Test_DdtraceSchemas::test_library was green on ETP/v1 payloads not because they were valid,
but because nothing was ever checked. Every v1 payload bypassed schema validation entirely,
including the fields the v1 format changed — which is exactly where a tracer is most likely to
deviate.

Changes

tests/schemas/utils/library/v1.0/traces-request.json (new). No wiring code is needed: the
schema id is derived from the path, so the file being present at the conventional location is
the wiring — same as every other trace endpoint. Conventions followed: $id equal to the path
below tests/schemas/utils (asserted by _get_schemas_store), the internal definitions +
#/definitions/... style of v0.7/config-request.json, and the per-field descriptions of
v0.6/stats-request.json.

The schema describes the payload as deserialize_v1_trace produces it, which is the whole
tracer payload and not a single trace: payload metadata → chunks[]spans[]
span_links[] / span_events[], plus one attributes definition shared by all five levels.
Worth noting for review, since it is easy to conflate the two: the top-level spans /
trace_id / priority / sampling_mechanism that tests/test_v1_payloads.py asserts on live
on the chunk, because DataDogLibraryTrace.from_v1 is called once per chunk in
utils/interfaces/_library/core.py:109.

The structural v1-vs-v0.4/v0.5 differences are enforced rather than described:

v0.4 / v0.5 v1.0
span tags meta + metrics single attributes object
trace_id integer ^0x[0-9A-F]{32}$ (16 raw bytes, hex encoded by the deserializer)
error integer boolean
span_kind string in meta integer 0–5 (SpanKind)

chunks[] requires trace_id + spans, spans require name (as v0.4 does), span links require
trace_id + span_id, span events require name + time_unix_nano. Well-known attributes are
typed where v1 post-processing guarantees a shape: _dd.appsec.json object, _dd.span_links
array, _dd.top_level number.

tests/test_the_test/test_deserializer.py — added
test_deserialize_v1_trace_validates_against_schema: it runs a payload exercising every
documented key through the real deserialize_v1_trace and the real SchemaValidator, then
asserts that a v0.4-style integer error is reported as $.chunks[].spans[].error. Without
it the schema would only ever run inside APM_TRACING_EFFICIENT_PAYLOAD (docker + a v1-capable
tracer); TEST_THE_TEST runs on every PR.

Verification

  • ./format.sh --check: mypy (528 files), ruff format, ruff check, trailing whitespace all
    pass. yamllint and utils/manifest/validate.py pass too (run separately — locally the script
    aborts earlier on a pre-existing yamlfmt disagreement about manifests/python.yml, a file
    this PR does not touch).
  • ./run.sh TEST_THE_TEST: 366 passed. The only 2 failures are test_load_binary.py::Test_LoadBinaryC,
    which fail identically on main on macOS (bash 3.2 vs "${array[@]}" under set -u).
  • tests/schemas/test_schemas.py::Test_DdtraceSchemas::test_library collects in
    APM_TRACING_EFFICIENT_PAYLOAD, confirming the schema is reached there. I could not execute
    that scenario locally (needs a built weblog + a v1-capable tracer), so instead I drove the
    repo's own SchemaValidator over 4 valid and 24 hand-built invalid payloads, JSON
    round-tripped through the proxy's ObjectDumpEncoder exactly as the runner does.

Notes for the reviewer

The field most likely to surface a surprise is error: boolean. I typed it from the v1 RFC,
from the deserializer's own fixture (8: True), and from the "Error field must be boolean"
assertion in tests/test_v1_payloads.py — but no existing test can actually tell bool from int
there, because Python evaluates True == 1 as true. If a tracer sends an integer, this schema
will be the first thing to say so; the fix is then either loosening that one field or a
SchemaBug entry with a real ticket.

Two deliberately loose spots, both driven by deserializer behaviour rather than by the protocol:

  • span link trace_state accepts an integer, because _uncompress_span_links_list keeps the raw
    string-table index alongside the resolved tracestate it adds;
  • there is no top-level required, because deserialize_v1_trace returns {} as its sentinel
    for a payload it cannot parse (it already warns), and the schema should not double-report that
    as a missing chunks.

Scope-wise this only touches tests/, and only adds — no scenario, manifest or framework change.

/v1.0/traces had no schema file, and SchemaValidator silently skips any
endpoint whose /library/<path>-request.json is missing, so every ETP/v1
payload bypassed schema validation entirely.

Describe the payload as deserialize_v1_trace produces it: payload metadata,
chunks[], spans[], span_links[], span_events[], and the attributes object
shared by all of them. The structural differences with v0.4/v0.5 are
enforced: a single attributes object instead of meta/metrics, hex encoded
string trace ids, boolean error, and integer span_kind.

Also add a TEST_THE_TEST test that runs a full featured payload through the
real deserializer and SchemaValidator: the schema is otherwise only
exercised by APM_TRACING_EFFICIENT_PAYLOAD, which needs docker and a
v1-capable tracer.

Co-Authored-By: Claude <noreply@anthropic.com>
@github-actions

github-actions Bot commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

CODEOWNERS have been resolved as:

tests/schemas/utils/library/v1.0/traces-request.json                    @DataDog/system-tests-core
tests/test_the_test/test_deserializer.py                                @DataDog/system-tests-core
utils/proxy/traces/trace_v1.py                                          @DataDog/system-tests-core

@datadog-datadog-prod-us1

datadog-datadog-prod-us1 Bot commented Aug 4, 2026

Copy link
Copy Markdown

Pipelines  Tests

⚠️ Warnings

🚦 2 Pipeline jobs failed

Testing the test | System Tests (php, dev) / End-to-end #2 / laravel11x 2   View in Datadog   GitHub Actions

🔧 Fix in code. This looks caused by changes in this PR. Assertion Error: Test failed at tests/stats/test_stats.py:489.

🧪 1 Test failed · 🎯 related to the job error above

🎯 tests.stats.test_stats.Test_Peer_Tags.test_peer_tags[laravel11x] from system_tests_suite   View in Datadog
AssertionError: Client spans should have peer tags, found: []
assert 0 &gt; 0
 &#43;  where 0 = len([])

self = &lt;tests.stats.test_stats.Test_Peer_Tags object at 0x7f2fc4564080&gt;

    def test_peer_tags(self):
        &#34;&#34;&#34;Test that client spans include peer tags while server spans don&#39;t&#34;&#34;&#34;
        client_stats_found = False
        server_stats_found = False
...

Testing the test | all-jobs-are-green   View in Datadog   GitHub Actions

See error Some CI checks or statuses failed during the execution of end-to-end tests.
📋 Copy prompt for your agent
CI on my pull request is failing. Help me find and fix the root cause of each failing job below — they were flagged as caused by changes in this PR, so focus on the diff. For each job, explain the failure and propose a fix.

Branch: dario.castane/tender-bassi-343ee0

Testing the test | System Tests (php, dev) / End-to-end #2 / laravel11x 2
Commit: b6458a50af7f37bb24b725b513724acbc7f445e1
Error (code / test):
Assertion Error: Test failed at tests/stats/test_stats.py:489.
CI job: https://github.com/DataDog/system-tests/actions/runs/31199317300/job/92990570909

ℹ️ Info

No other issues found (see more)

❄️ No new flaky tests detected

🔄 Datadog auto-retried 2 jobs - 2 passed on retry View in Datadog

Useful? React with 👍 / 👎

This comment will be updated automatically if new data arrives.
🔗 Commit SHA: 8525694 | Docs | Datadog PR Page | Give us feedback!

darccio and others added 3 commits August 6, 2026 12:45
…unk origin

The new /v1.0/traces schema exposed a deserializer bug: _uncompress_keys
never resolved integer string-table references for top-level fields
(app_version, container_id, ...), and _uncompress_chunks only checked the
origin ref-resolution condition under the spans branch, which is never
true for the origin key itself. Tracers that stream these fields as
string-table indices (e.g. golang, java native image) surfaced as
schema errors like "3 is not of type 'string' on instance $.app_version".

Co-Authored-By: Claude <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant