PQ CRYPTA PLATFORM

🏠 Main

🧪 Interactive Apps

📰 News

🛡️ PQ Crypta Proxy

👤 Account

⟨ QUANTUM ERROR PORTAL ⟩

Navigate the Error Dimensions

PQ Crypta Logo

HTTP/3 Client Conformance

A deliberately broken QUIC and HTTP/3 server, for testing clients

We do not ask your client whether it handled the protocol correctly. We watch what it actually did on the wire.

Point a client library at it and the server misbehaves on purpose — a reserved frame type, a duplicated setting, a control stream opening with the wrong frame — then records how your client answered. Every verdict comes from what the server observed, because a library that crashes on an unknown frame is in no position to report on itself. Free, no account, open to any client.

The QUIC Interop Runner tests implementations against each other in a lab matrix. This is the other thing: the awkward corners, on demand, against your client.

Results for several well-known implementations are published in the client matrix — what curl, Chromium, quic-go, aioquic and quinn each do when the server misbehaves.

Unlike an interoperability test, this one deliberately sends your client protocol edge cases, illegal messages, GREASE, awkward state transitions and hostile network conditions — and judges the result from the wire, not from what the client reports about itself.

Offering one requires a server that will misbehave on demand — emit a reserved frame type, duplicate a SETTINGS identifier, open a control stream with the wrong frame. That is not something a CDN customer can arrange, because they do not own the implementation. We fork the QUIC stack, so we can.

51 tests
28 HTTP/3 layer
23 QUIC layer
4460–4510 UDP ports

What the catalogue covers

QUIC

23 tests

correctness
8
discretionary
7
resilience
5
interoperability
2
extensibility
1

HTTP/3

28 tests

correctness
14
interoperability
5
discretionary
4
extensibility
3
resilience
2

What a result means

PassThe client demonstrated the behaviour the specification requires.
FailThe client did something the specification prohibits.
InconclusiveThe run could not establish the observation — your client was never put in the situation the test is about. Never counted either way.

Running it

Each test has its own UDP port. Connect to it, and the server records what your client did. Results are filed under a session started from your address, so the test connections need nothing special.

A port per test rather than a path per test, because several anomalies are properties of the endpoint rather than of a connection — the set of QUIC versions offered is decided before any connection exists, so a test of it has to own the whole port.

# start a session
SESSION=$(curl -sX POST https://conformance.pqcrypta.com/session | jq -r .id)

# walk the catalogue
curl -s https://conformance.pqcrypta.com/catalog.json \
  | jq -r '.tests[].port' | while read PORT; do
      curl -s --http3-only --max-time 15 \
        "https://conformance.pqcrypta.com:$PORT/" -o /dev/null
    done

# read the result
curl -s https://conformance.pqcrypta.com/report/$SESSION.json | jq .totals

Without jq: grep -o '"port":[0-9]*' | cut -d: -f2 pulls the ports out of the catalogue, and the session id is the first "id" in the /session response.

The report is also available as a page at /report/<session> and as an SVG badge at /badge/<session>.svg.

In CI

h3-conformance does the above and exits non-zero if anything failed. It is a harness rather than a client: it runs a command you supply, once per test, so whatever your library connects with is what gets measured.

# drive curl
h3-conformance

# drive your own client
h3-conformance --client 'my-client --url {url}'

# one area, machine-readable
h3-conformance --filter h-qpack --json

Exit 0 clean, 1 a test failed, 2 the suite was unreachable — distinct so an outage never reads as a regression in your client. Inconclusive results never fail the run.

OptionDefaultWhat it does
--hostconformance.pqcrypta.comWhich suite to run against.
--clientcurlCommand run once per test. {url} and {port} are substituted; its exit status is ignored, because a client failing a test frequently should exit non-zero and that is a result, not an error.
--filterRun only tests whose id contains this, e.g. h-qpack.
--timeout30Seconds to allow each client invocation before giving up on it.
--jsonoffPrint the report as JSON instead of text.
--quietoffOnly print failures and the summary.

The report

A session collects results as you walk the ports, and is readable three ways: as a page at /report/<session>, as JSON at /report/<session>.json, and as an SVG badge at /badge/<session>.svg. All three are public and CORS-open, so CI can fetch them without a key.

{
  "session": "8fbecf53c82f7c6481ecca51727c6742",
  "catalog_size": 51,
  "totals":   { "pass": 22, "fail": 0, "inconclusive": 5, "not_run": 0 },
  "by_class": { "correctness": { "pass": 6, "fail": 0, "inconclusive": 3 }, ... },
  "results": [
    { "id": "h-grease-frame", "class": "extensibility", "tier": "http3",
      "spec": "RFC 9114 §7.2.8", "verdict": "pass",
      "detail": "Ignored the unrecognised element and completed the follow-up request." }
  ]
}

Test ids are stable and appear in the URL, the JSON and the badge, so they are safe to pin a CI assertion to.

Embedding the badge

![HTTP/3 conformance](https://conformance.pqcrypta.com/badge/$SESSION.svg)

A badge is a picture of one run, not a live status: re-run the suite and use the new session to refresh it.

How results find their session

By default results are filed under the session started from your address, which is why the walk above needs nothing special. If your tests run from several machines, or from behind a shared address, connect with the server name <session>.conformance.pqcrypta.com instead — the session travels in the SNI, and results follow it rather than the address.

A session expires an hour after it was created, not an hour after its last result — activity does not extend it. Start the session at the beginning of the walk rather than long before, and fetch the report while the run is fresh; nothing is archived.

How a verdict is reached

The server is the judge. A library that crashes on an unknown frame is in no position to report on itself, so every verdict comes from what the server observed.

Every test ends with a liveness probe: after the anomaly, the server expects one ordinary request on the same connection. For the extensibility tests this is the whole game — "ignored it" and "died on it" look identical on the wire up to that point, and without the probe a crashed client would read as compliant.

A failure needs positive evidence. It is tempting to read “no rejection arrived” as “the client accepted it”, and that inference is unsound. Where the anomaly rides in the response your client had to read to be served, a completed exchange does show it was seen. Where it went to the control stream — unidirectional, and read on no particular schedule — a one-shot request can finish before the stream is picked up at all, which looks identical to accepting the violation. The same is true of a rejection whose CONNECTION_CLOSE is lost in transit, since QUIC only repeats that frame in answer to an incoming packet. Neither case is reported as a failure.

The classes exist because the required answer is not the same everywhere, and sometimes it is the opposite. Ignoring a reserved HTTP/3 frame is a pass — reserved frame types exist precisely to be ignored. Ignoring a reserved QUIC frame is a failure, because QUIC reserves no ignorable frame types and an unknown frame carries no length to skip it by. Two tests that look alike on the wire require opposite behaviour, so a single pass/fail rule would score one of them backwards.

Extensibility

Ignore something unrecognised and carry on. Rejecting it is the failure — that is how protocols ossify.

Correctness

Reject something invalid, with the specific error code the RFC names. Accepting it is the bug — but a failure here is only recorded when your client demonstrably read the anomaly, never merely because no rejection arrived.

Interoperability

Correctly decode something valid but demanding — Huffman-coded fields, a dynamic-table reference, trailers.

Resilience

Recover — retry, re-probe, migrate — rather than give up.

Discretionary

The RFC permits more than one answer. Both pass; the report says which yours chose.

Inconclusive

The run never put your client in the situation the test is about. It proves nothing either way, and says so.

The catalogue

Read live from the suite, so this table cannot drift from what is actually served. Every entry cites the clause it exercises.

HTTP/3 layer

Test Port Class Specification
h-grease-settings Reserved SETTINGS identifier (0x1f¡N+0x21) Ignore the unknown setting and complete the request. 4473 Extensibility RFC 9114 §7.2.4.1
h-grease-frame Reserved frame type on the response stream Skip the frame using its length and read the response that follows. 4474 Extensibility RFC 9114 §7.2.8
h-reserved-uni-stream Unidirectional stream with a reserved stream type Abort reading the stream or discard it — §6.2.3 permits either. What it forbids is treating it as meaningful, or as fatal to the connection. 4475 Extensibility RFC 9114 §6.2.3
h-duplicate-setting SETTINGS containing the same identifier twice Either reject with H3_SETTINGS_ERROR or ignore the repeat — the specification says a receiver MAY treat this as an error, so both are conformant. 4476 Discretionary RFC 9114 §7.2.4
h-control-frame-unexpected A DATA frame on the control stream Close the connection with H3_FRAME_UNEXPECTED. 4477 Correctness RFC 9114 §7.2.1
h-missing-settings Control stream whose first frame is not SETTINGS Close the connection with H3_MISSING_SETTINGS. 4478 Correctness RFC 9114 §6.2.1
h-second-control-stream A second control stream opened by the server Close the connection with H3_STREAM_CREATION_ERROR. 4479 Correctness RFC 9114 §6.2.1
h-qpack-dynamic-table Field lines referencing dynamic table insertions Apply the encoder-stream insertions and decode the headers correctly. 4480 Interoperability RFC 9204 §4.3.3, §4.5.2
h-qpack-huffman Huffman-coded field lines with maximal padding Decode without error. Padding of up to 7 bits is legal, not corruption. 4481 Interoperability RFC 9204 §4.1.2, RFC 7541 §5.2
h-oversized-field-section Field section larger than the client's advertised maximum Handle it as an error against that one request, not the whole connection. 4482 Interoperability RFC 9114 §4.2.2
h-trailers Trailing field section after the body Deliver the trailers to the application after the body completes. 4483 Interoperability RFC 9114 §4.1
h-early-hints 103 Early Hints before the final response Treat 103 as informational and keep reading for the final response. RFC 9110 §15.2 makes this a MUST: a client "MUST be able to parse one or more 1xx responses received prior to a final response, even if the client does not expect one". Ignoring the hints is fine — a user agent MAY do that — but closing the connection is not. 4484 Resilience RFC 9110 §15.2, RFC 8297
h-goaway GOAWAY sent mid-connection Stop opening requests, finish those in flight, and retry idempotent ones elsewhere. 4485 Resilience RFC 9114 §5.2
h-max-push-id MAX_PUSH_ID sent by the server Reject with H3_FRAME_UNEXPECTED. MAX_PUSH_ID travels client to server only, so a server sending one is using a frame in a direction the specification does not allow. 4486 Correctness RFC 9114 §7.2.7
h-settings-on-request-stream SETTINGS frame on a request stream Close the connection with H3_FRAME_UNEXPECTED. SETTINGS belongs to the control stream alone: §7.2.4 says that if an endpoint receives one on a different stream it "MUST respond with a connection error of type H3_FRAME_UNEXPECTED". 4490 Correctness RFC 9114 §7.2.4
h-data-before-headers DATA frame before any HEADERS on the response stream Close the connection with H3_FRAME_UNEXPECTED. A response begins with a field section, and §4.1 makes "receipt of an invalid sequence of frames" a connection error of that type — a body arriving before the headers that describe it is exactly that. 4491 Correctness RFC 9114 §4.1
h-cancel-push-unsolicited CANCEL_PUSH for a push ID that was never promised Close the connection with H3_ID_ERROR. No MAX_PUSH_ID was granted, so every push ID is greater than currently allowed, and §7.2.3 requires a CANCEL_PUSH referencing one to be treated as a connection error of that type. 4492 Correctness RFC 9114 §7.2.3
h-qpack-blocked-stream Field section that blocks until the encoder stream catches up Hold the field section, apply the insertions when they arrive on the encoder stream, and complete the request. §2.2.1 makes a section whose Required Insert Count exceeds the decoder's Insert Count a blocked stream — something to be waited on, not an error. Only run when the client advertised both a table capacity and at least one blocked stream; §2.1.2 forbids the encoder from blocking more streams than the decoder promised to support, so a client that permits none cannot be tested on this. 4493 Interoperability RFC 9204 §2.1.2, §2.2.1
h-response-stream-reset Response stream reset mid-body with H3_REQUEST_CANCELLED Abandon the partial response — §4.1 says a response cancelled after a partial delivery "SHOULD NOT be used". Whether the connection survives is the client's to choose: §8 lets an endpoint "treat a stream error as a connection error under certain circumstances", so keeping the connection and closing it are both conformant. Stalling is the one wrong answer. 4494 Discretionary RFC 9114 §8, §4.1
h-priority-update PRIORITY_UPDATE sent by the server Reject it with H3_FRAME_UNEXPECTED, or ignore it — which is conformant depends on whether you implement extensible priorities at all, and that is not observable from here. A client that does implement RFC 9218 is bound by §7.2: servers "MUST NOT send PRIORITY_UPDATE frames of either type", and a client receiving one MUST treat it as a connection error of that type. A client that does not implement it sees frame type 0xf0700 as simply unknown, and RFC 9114 §9 requires unknown frame types to be ignored — so ignoring it is equally correct, for a different reason. Scoring this as a failure either way would accuse one of those two clients of a violation it did not commit, so the report says which answer was given rather than grading it. 4498 Discretionary RFC 9218 §7.2
h-extended-connect SETTINGS_ENABLE_CONNECT_PROTOCOL with a value outside 0 and 1 Reject it or ignore it, but keep working. RFC 8441 §3 says the value "MUST be 0 or 1" and RFC 9220 carries that into HTTP/3 unchanged — yet neither names a behaviour for a receiver that sees anything else, so both answers are conformant and stalling is not. The setting is how a client learns Extended CONNECT is available, so a WebTransport-capable client has real parsing behind it. 4499 Discretionary RFC 9220 §3, RFC 8441 §3
h-push-promise-unsolicited PUSH_PROMISE for a push the client never allowed Close the connection with H3_ID_ERROR. §7.2.7 leaves the maximum push ID unset until the client sends MAX_PUSH_ID, so a server "cannot push until it receives a MAX_PUSH_ID frame" and every push ID is larger than the client has advertised. §7.2.5 is explicit about the answer: a client "MUST treat receipt of a PUSH_PROMISE frame that contains a larger push ID than the client has advertised as a connection error of H3_ID_ERROR". 4500 Correctness RFC 9114 §7.2.5, §4.6
h-datagram-setting-invalid SETTINGS_H3_DATAGRAM with a value that is neither 0 nor 1 Close the connection with H3_SETTINGS_ERROR. Unusually for a setting, RFC 9297 pins down the invalid-value case rather than leaving it open: the value "MUST be either 0 or 1", and if one "is received with a value that is neither 0 nor 1, the receiver MUST terminate the connection with error H3_SETTINGS_ERROR". This is the setting that gates HTTP Datagrams and so WebTransport, which means a client that implements either has real parsing behind it rather than an ignored identifier. 4501 Correctness RFC 9297 §2.1.1
h-qpack-encoder-overflow Encoder stream setting a dynamic table capacity above the client's limit Close the connection with QPACK_ENCODER_STREAM_ERROR (0x201). §4.3.1 says the new capacity "MUST be lower than or equal to the limit" the decoder advertised, and that a decoder "MUST treat a new dynamic table capacity value that exceeds this limit as a connection error of type QPACK_ENCODER_STREAM_ERROR". Unlike the other two dynamic-table tests, this one runs against every client: they all advertise a capacity of zero, and any capacity at all exceeds a limit of zero. 4502 Correctness RFC 9204 §4.3.1, §6
h-goaway-increasing A second GOAWAY naming a larger identifier than the first Close the connection with H3_ID_ERROR. §5.2 permits multiple GOAWAY frames but requires the identifier in each to be no greater than any previously sent, because the identifier is a promise about what will still be processed and raising it takes that promise back. "Receiving a GOAWAY containing a larger identifier than previously received MUST be treated as a connection error of type H3_ID_ERROR." 4503 Correctness RFC 9114 §5.2
h-push-stream-unpromised Push stream opened for a push nobody allowed Close the connection with H3_ID_ERROR. §6.2.2 does not require the push to have been promised first — the stream alone is enough: a client "MUST treat receipt of a push stream as a connection error of type H3_ID_ERROR when no MAX_PUSH_ID frame has been sent", and no client under test sends one. Distinct from the PUSH_PROMISE test, which puts the same violation in a frame on the control stream. This one opens the push stream itself, which a client has to recognise by its stream type before any frame inside it is read. 4504 Correctness RFC 9114 §6.2.2
h-qpack-static-index-invalid Field line indexing a static table entry that does not exist Close the connection with QPACK_DECOMPRESSION_FAILED (0x200). The static table has 99 entries, so index 200 refers to nothing, and §3.1 is explicit: "When the decoder encounters an invalid static table index in a field line representation, it MUST treat this as a connection error of type QPACK_DECOMPRESSION_FAILED." The static table needs no permission and never changes size, so unlike the dynamic-table tests this one applies to every client. 4505 Correctness RFC 9204 §3.1, §4.5.2
h-qpack-encoder-bad-name-index Encoder instruction naming a static index that does not exist Close the connection with QPACK_ENCODER_STREAM_ERROR (0x201). The same bad index means different things depending on where it arrives, and §3.1 says both: on a field line it is QPACK_DECOMPRESSION_FAILED, and "if this index is received on the encoder stream, this MUST be treated as a connection error of type QPACK_ENCODER_STREAM_ERROR". Paired deliberately with the field-line version on the neighbouring port: a client that answers both with the same code has collapsed a distinction the specification draws twice in one paragraph. 4507 Correctness RFC 9204 §3.1, §4.3.2

QUIC layer

Test Port Class Specification
q-version-negotiation Version Negotiation offering a reserved version alongside v1 Abandon the connection attempt, or retry with a version both ends support. §6.2 requires a client that supports only one version to abandon it rather than persist. 4460 Correctness RFC 9000 §6
q-retry Retry packet for source-address validation Echo the Retry token in a second Initial packet and complete the handshake. 4461 Correctness RFC 9000 §8.1.2
q-reserved-transport-param Reserved transport parameter (31¡N+27) Ignore the unknown parameter and complete the handshake normally. 4462 Extensibility RFC 9000 §18.1
q-reserved-frame Unknown frame type in a 1-RTT packet Close the connection with FRAME_ENCODING_ERROR. Unlike HTTP/3, QUIC reserves no ignorable frame types — §12.4 makes an unknown frame a connection error, so ignoring it is the failure here. 4463 Correctness RFC 9000 §12.4
q-cid-rotation NEW_CONNECTION_ID followed by RETIRE_CONNECTION_ID Adopt the new connection ID, retire the old one, and stay connected. 4464 Resilience RFC 9000 §5.1
q-stateless-reset Stateless reset Recognise the token in the last 16 bytes of the datagram, enter the draining period, and send no further packets on the connection. The packet cannot be authenticated, so there is nothing to reply with and nothing to report to the peer — continuing to send is the failure §10.3.1 names. 4465 Correctness RFC 9000 §10.3
q-flow-control Deliberately tight MAX_DATA and MAX_STREAM_DATA Respect the limit. Announcing the stall with DATA_BLOCKED or STREAM_DATA_BLOCKED is a SHOULD in §4.1, not a MUST, so a client that stays silent is still conformant. 4466 Discretionary RFC 9000 §4
q-ack-frequency ACK Frequency extension offered Negotiate the extension, or ignore it. Either is correct; failing is not. 4467 Discretionary draft-ietf-quic-ack-frequency
q-ecn Packets marked ECT(0) Echo the ECN counts back in ACK frames carrying an ECN section (type 0x03). §13.4.1 makes this a conditional requirement — an endpoint MUST report the markings it receives "if these are accessible", and explicitly permits an endpoint with no access to the ECN field to report nothing. So counts coming back is a pass, and silence is inconclusive rather than a failure: it cannot be told apart from a path that stripped the codepoint in transit. 4468 Correctness RFC 9000 §13.4
q-pmtu-blackhole Path MTU black hole above a threshold Detect the black hole, probe down to a working size, and keep the connection. Path-MTU discovery is driven past the limit on this port, so it engages on every connection. 4469 Resilience RFC 9000 §14, RFC 8899
q-path-challenge Server-initiated PATH_CHALLENGE Reply with PATH_RESPONSE carrying the identical 8-byte payload. 4470 Correctness RFC 9000 §8.2
q-zero-rtt-reject 0-RTT rejected after the client sends early data Reset the state of every stream, including application state bound to them. Section 4.6.2 requires the reset because a rejected 0-RTT means every assumed connection characteristic may have been wrong. It does not require retransmission, which is the application concern, not QUIC's. This port issues tickets that advertise early data and then declines every offer, so a resuming client sends 0-RTT and always has it refused. The handshake itself completes normally. 4471 Resilience RFC 9001 §4.6.2
q-multipath A second path offered mid-connection Use the additional path, or decline it cleanly. Do not abort the connection. 4472 Discretionary draft-ietf-quic-multipath
q-key-update Spontaneous 1-RTT key update Update to the next key phase and carry on. Once a packet protected with the next phase is processed, §6.2 is a MUST — "The endpoint MUST update its send keys to the corresponding key phase in response" — so the response written after the update has to be read with the new keys and the request completed. 4487 Interoperability RFC 9001 §6.2
q-stream-limit Stream limits set to the minimum a request needs Stay inside the advertised limits. Respecting them is a MUST — §4.6 says "Endpoints MUST NOT exceed the limit set by their peer" — but announcing the stall with STREAMS_BLOCKED is only a SHOULD, so a client that stays silent is still conformant. 4488 Discretionary RFC 9000 §4.6
q-loss-recovery One datagram in twelve dropped once the path is established Reassemble the stream and deliver the whole body. §2.2 requires an endpoint to buffer data received out of order and deliver it as an ordered byte stream, and §13.3 has the lost data sent again in new STREAM frames — so a response full of gaps must still arrive complete and in order. 4489 Resilience RFC 9000 §2.2, §13.3
q-connection-migration Datagrams arriving from a second server address Keep using the address you are already talking to. §9.6 says a client "SHOULD ignore packets received from a server address other than the one it is currently using for sending packets" — a SHOULD, so quietly discarding them and objecting are both conformant. Following the new address is not: nothing is listening there, and a server may only move a client to an address it advertised as its preferred one. 4495 Discretionary RFC 9000 §9.6
q-invalid-transport-param Transport parameter carrying a value the specification forbids Close the connection with TRANSPORT_PARAMETER_ERROR. §18.2 makes an ack_delay_exponent above 20 invalid, and §7.4 is a MUST: "An endpoint MUST treat receipt of a transport parameter with an invalid value as a connection error of type TRANSPORT_PARAMETER_ERROR." Distinct from a *duplicate* parameter, which the same clause makes only a SHOULD — this port sends one parameter, once, with a value outside its permitted range. The parameter travels in the handshake, so every client that connects here reads it. Only a client that answers with a CONNECTION_CLOSE this endpoint can read is scored: one that simply abandons the handshake is recorded as inconclusive, because a close that was sent and lost looks exactly like one that was never sent. 4496 Correctness RFC 9000 §7.4, §18.2
q-zero-rtt-replay 425 (Too Early) in answer to a request sent as early data Handle being told the request arrived too early. §5.2 says a user agent "SHOULD retry automatically, but any retries MUST NOT be sent in early data" — so retrying on the 1-RTT keys and handing the 425 back to the caller are both conformant, and the report says which happened. Falling over is not one of the options. This is the only port that *accepts* early data instead of refusing it, which is what makes the 425 exchange possible at all. What it does not check is §4's rule that unsafe methods must never be sent in early data: reading the method would mean QPACK-decoding the request, which this suite deliberately never does. Reaching it needs a session ticket from an earlier connection to this same port, so a client that connects once has none. 4497 Discretionary RFC 8470 §5.2
q-ecn-congestion Path marking packets CE, not just ECT(0) Report the CE count back in the ECN section of an ACK. §13.4.1 makes this the same conditional requirement as reporting ECT(0) — an endpoint MUST provide feedback about the markings it receives "if these are accessible" — and CE is the marking that matters, since it is how a path says it is congested. Distinct from the ECT(0) test on the neighbouring port: that one asks whether a client reports markings at all, and this asks whether it distinguishes the one that means something. A client that echoes ECT(0) counts faithfully and never reports a CE has a congestion signal it cannot see. Silence is inconclusive, not a failure, for the same reason: the ECN field may be inaccessible to the peer, and a path that rewrites the codepoint in transit is indistinguishable from here. 4508 Correctness RFC 9000 §13.4
q-key-update-repeated A second key update after the first is acknowledged Follow both updates and carry on. §6.1 lets an endpoint update again once the previous phase has been acknowledged, so a long-lived connection changes keys repeatedly and a client has to track the phase rather than assume one change. Distinct from the single update on the neighbouring port. An implementation that hardcodes the first transition — treating key phase as a one-way flag rather than a bit that alternates — passes that test and fails here, which is precisely the bug worth finding. 4509 Interoperability RFC 9001 §6.1, §6.5
q-max-streams-credit No stream credit at first, then MAX_STREAMS mid-connection Wait for the credit, then open the request. This port grants no bidirectional streams in its transport parameters and issues MAX_STREAMS a moment later. §4.6 is unambiguous that a client may not jump the gun — "Endpoints MUST NOT exceed the limit set by their peer" — and announcing the wait with STREAMS_BLOCKED is a SHOULD, so a client that waits quietly is equally conformant. Giving up rather than waiting is not scored as a failure: nothing obliges a one-shot client to sit on a connection it cannot use yet, and the report says which it did. 4510 Discretionary RFC 9000 §4.6
q-packet-reordering Datagrams delivered out of order Put the stream back in order and deliver the whole body. §2.2 requires an endpoint to be "able to deliver stream data to an application as an ordered byte stream", and says plainly that doing so "requires that an endpoint buffer any data that is received out of order". Distinct from the loss test, which needs retransmission before the gap can be filled. Nothing is lost here: every byte arrives, some of it early, and a client that assumes arrival order is delivery order will produce a corrupt body or stall waiting for data it already has. 4506 Resilience RFC 9000 §2.2

When a run proves nothing

Every port in the catalogue emits its anomaly—51 of 51. Nothing here is a placeholder.

Whether your run reaches a given anomaly is a separate question, and some depend on what your client happens to do. A few examples, all of which report inconclusive rather than guessing:

An inconclusive result says which of these applied. It is never counted as a pass and never as a failure, and it does not fail a CI run.

If your client scores a failure you believe is wrong, say so — a suite that accuses the thing it tests is worth fixing quickly.

Scope, and what this is not

Fifty-one tests is a beginning, not coverage. QUIC and HTTP/3 together are enormous, and the catalogue is chosen rather than exhaustive. What is missing divides into two quite different things, and collapsing them into one list of “not done yet” would overstate what this suite can ever become.

Testable, and not yet written

The backlog is shorter than it was: ECN congestion marking, repeated key updates and mid-connection MAX_STREAMS credit have since been built. What is left is a Retry packet a client ought to discard — RFC 9000 §17.2.5.2 has a client ignore any Retry after the first — and a handful of similar single-clause cases.

Compatible version negotiation is a case of its own, and no longer counted as backlog. RFC 9000 §6.2 does not define what a client does when the offered version is one it supports; it says only that “how to perform version negotiation is left as future work”. RFC 9368 supplies that work, so the test becomes possible once the stack implements version_information — and not before. Writing it now would mean measuring behaviour no clause requires, which is how a conformance suite starts inventing requirements.

Outside what this vantage point can establish

Not a backlog, and listing it as one would be misleading. These are behaviours a server watching a client cannot legitimately provoke or observe, so a test claiming to cover them would be reporting on something other than what it says.