Skip to content

Fix DX-5356 + DX-5421, classify 5 deferred bugs as server-side#22

Merged
johnpmitsch merged 2 commits into
mainfrom
dx-5040-sdk-bug-sweep-2
May 20, 2026
Merged

Fix DX-5356 + DX-5421, classify 5 deferred bugs as server-side#22
johnpmitsch merged 2 commits into
mainfrom
dx-5040-sdk-bug-sweep-2

Conversation

@johnpmitsch

@johnpmitsch johnpmitsch commented May 19, 2026

Copy link
Copy Markdown
Collaborator

Summary

Second pass on the DX-5040 SDK bug sweep. Fixes two more SDK-side bugs and classifies five "silent no-op" / "ignored field" bugs as server-side via durable wire-inspection regression tests.

SDK fixes

  • DX-5356TestFilterParams.filter_function is now String (required) instead of Option<String>. The API rejects requests without it, so the type now matches the API contract. Mirrored across the Python pyo3 signature, Ruby hash validator + RBS sig, the Rust example, and existing test fixtures.
  • DX-5421EndpointMetric.tag is now Vec<String> with a custom tag_as_vec deserializer that accepts both wire shapes. The metrics endpoints return tag as either a plain string for single-axis series ("total", "p95") or a [key, value] tuple for multi-axis series (["network", "arbitrum-mainnet"]); the previous tag: String declaration failed every multi-axis response with DecodeError. Surfaces as Array<string> (TS), list[str] (Python), and Array<String> (Ruby) — bindings regenerated automatically. Includes 5 unit tests plus a wiremock regression that replays the exact bug-report body.

Bugs classified as server-side

Added 5 wiremock regression tests that assert the outgoing request body. All 5 pass, confirming the SDK sends the correct wire format — so the failures are server-side, not serde misconfigurations:

Bug Linear Wire-inspection assertion
0002 DX-5341 bulk_sets sends add_sets
0003 DX-5342 bulk_sets sends delete_sets
0004 DX-5343 update_list sends add_items + remove_items
0008 DX-5347 update_webhook_template sends name
0013 DX-5352 update_team_endpoints sends endpoint_ids: [] when given an empty Vec

The tests stay in the suite as durable regressions so future serde changes that rename or drop these fields fail loudly.

Housekeeping

  • Adds a CLAUDE.md rule: no Linear IDs, PR numbers, or other internal tracker references in code comments. The "why" should be self-contained; tracker context belongs in commit messages or PRs.
  • Removes the 4 pre-existing DX-#### references from the wire-inspection regression comments added by the earlier commit in this branch (kvstore × 2, admin × 1, webhooks × 1).

Recommended Linear action

For DX-5341, DX-5342, DX-5343, DX-5347, DX-5352: leave them open but reassign / re-team to the backend owner with a comment pointing at the wire-inspection test that proves the SDK side is correct.

Test plan

  • cargo check --workspace clean
  • just lint (clippy -D warnings) clean
  • cargo test -p quicknode-sdk --lib — 190 Rust tests pass (184 from sweep Beta #1 commit + 6 new from the DX-5421 fix: 5 unit + 1 wiremock regression)
  • just node-build (build + napi test) clean
  • just ruby-build clean
  • just python-build (stubs regenerated) clean
  • Dogfood against the sdk-smoke-tester e2e suite by pointing its @quicknode/sdk dep at this branch

Linear

Fixes DX-5356
Fixes DX-5421

DX-5356 — streams testFilter: filter_function is now required (String)
instead of Option<String> in TestFilterParams. The API rejects requests
without it, so the type now matches the API contract. Mirrored across
the Python pyo3 signature, Ruby hash validator, RBS sig, examples, and
existing test fixtures.

Adds 5 wiremock regression tests that capture the outgoing request body
for the 4 silent-no-op / ignored-field bugs and confirm the SDK is
sending the correct wire format. All 5 pass, meaning the failures are
server-side (or upstream contract issues), not SDK bugs:

  - DX-5341: bulk_sets sends `add_sets` correctly
  - DX-5342: bulk_sets sends `delete_sets` correctly
  - DX-5343: update_list sends `add_items` and `remove_items` correctly
  - DX-5347: update_webhook_template sends `name` correctly
  - DX-5352: update_team_endpoints sends `endpoint_ids: []` (empty
    array) correctly when supplied with an empty Vec

The wire-inspection tests are kept in the suite as durable regressions
so future serde changes that drop or rename these fields fail loudly.
@linear

linear Bot commented May 19, 2026

Copy link
Copy Markdown
DX-5356 SDK Bug 0017 — streams: testFilter rejects requests without `filterFunction`/`addressBookConfig` despite optional typing

  • SDK version: 3.1.0-alpha.14
  • Section: streams
  • Method: qn.streams.testFilter
  • Discovered: 2026-05-17
  • Status: open
  • Failing test(s):
    • tests/streams/filters.test.ts > streams testFilter > testFilter without a filter function returns raw data
    • tests/streams/filters.test.ts > streams testFilter > testFilter on the Logs dataset
    • tests/streams/filters.test.ts > streams testFilter > testFilter on the Transactions dataset
    • tests/streams/filters.test.ts > streams testFilter > testFilter on the BlockWithReceipts dataset

Symptom

TestFilterParams.filterFunction and .addressBookConfig are both typed as optional in node_modules/quicknode/sdk/index.d.ts (lines 1546, 1550). In practice the API requires at least one of them to be supplied — any call that omits both yields:

400 Bad Request — "filter_function must be base64 encoded"

…or, with filterFunction: "":

400 Bad Request — "Invalid test filter input: please specify the filter_function field or address_book_config field"

Reproduction

import { QuicknodeSdk } from "@quicknode/sdk";
const qn = QuicknodeSdk.fromEnv();
await qn.streams.testFilter({
  network: "ethereum-mainnet",
  dataset: "Block",
  block: "17811625",
  // no filterFunction
  // no addressBookConfig
});

Observed

ApiError (400): "filter_function must be base64 encoded"

A subsequent call with filterFunction: "" returns the clearer error:

"Invalid test filter input: please specify the filter_function field or address_book_config field"

A call with a valid base64-encoded JS function succeeds.

Expected

Either:

  • the type contract should require at least one of filterFunction / addressBookConfig, or
  • the API should treat both-absent as "no transformation, return raw data" (which is what a consumer would reasonably expect given the optional typing).

The first error message ("must be base64 encoded") is also misleading when the field is simply absent — the field hasn't been encoded incorrectly, it just hasn't been provided.

Suspected cause

Same shape as bugs 0006, 0007, 0015: SDK type optional, upstream API treats it as required.

Notes

A trivial identity filter function main(data) { return data; } (base64: ZnVuY3Rpb24gbWFpbihkYXRhKSB7IHJldHVybiBkYXRhOyB9) works as a "no-op" workaround for consumers who want to inspect raw data on a block.


Discovered by the sdk-smoke-tester e2e suite. Bug file: bugs/0017-testfilter-filterfunction-typed-optional-but-required.md.

DX-5040 SDK v2 Beta

DX-5421 SDK Bug 0022 — admin: getAccountMetrics fails to decode `tag` field (server returns tuple, SDK expects string)

  • SDK version: 3.1.0-alpha.15
  • Section: admin
  • Method: qn.admin.getAccountMetrics
  • Discovered: 2026-05-18
  • Status: open
  • Failing test(s):
    • tests/admin/metrics.test.ts > admin account metrics > getAccountMetrics period=day metric=credits_over_time
    • tests/admin/metrics.test.ts > admin account metrics > getAccountMetrics period=week metric=credits_over_time
    • tests/admin/metrics.test.ts > admin account metrics > getAccountMetrics period=month metric=credits_over_time
    • tests/admin/metrics.test.ts > admin account metrics > getAccountMetrics with method_calls_over_time

Symptom

getAccountMetrics throws a DecodeError because the SDK declares EndpointMetric.tag: string (node_modules/quicknode/sdk/index.d.ts line 212) but the API returns tag as a [string, string] tuple.

DecodeError: Failed to decode response: invalid type: sequence, expected a string at line 1 column 16
Body: {"data":[{"tag":["network","arbitrum-mainnet"],"data":[[1779109200,40]]}, …

Reproduction

import { QuicknodeSdk } from "@quicknode/sdk";
const qn = QuicknodeSdk.fromEnv();
await qn.admin.getAccountMetrics({ period: "day", metric: "credits_over_time" });
// throws DecodeError

Observed

Raw response body:

{
  "data": [
    { "tag": ["network", "arbitrum-mainnet"], "data": [[1779109200, 40]] },
    { "tag": ["network", "mainnet"],          "data": [[1779116400, 40]] }
  ],
  "error": null
}

The SDK's Rust core can't deserialise ["network", "arbitrum-mainnet"] into tag: String and throws before returning to the caller.

Expected

Either:

  • the SDK types tag as a [string, string] tuple (matching the server), or
  • the SDK accepts both shapes and presents a normalised value, or
  • the server returns a flat string like "network:arbitrum-mainnet" matching the SDK type.

Suspected cause

Mismatch between the Rust core's EndpointMetric struct (tag: String) and the actual server response shape. Likely a wire-format change on either side that wasn't reflected in the SDK schema. The getEndpointMetrics endpoint uses the same EndpointMetric type and may be similarly affected.

Notes

  • Discovered while running the smoke-tester against 3.1.0-alpha.15. The same response shape was almost certainly present in alpha.14 — these tests just weren't part of the prior sweep.
  • The latency tests (response_time_over_time with percentiles) appear to pass because that metric returns a single-axis series with a string tag.

Discovered by the sdk-smoke-tester e2e suite. Bug file: bugs/0022-getaccountmetrics-tag-decode-error.md.

Review in Linear

The metrics endpoints return `tag` as either a plain string (single-axis
series like `"total"` or `"p95"`) or a `[key, value]` tuple (multi-axis
series like `["network", "arbitrum-mainnet"]`). The SDK declared
`tag: String`, so multi-axis responses failed with DecodeError before
returning to the caller.

Change the field to `Vec<String>` with a custom `tag_as_vec`
deserializer that accepts both wire shapes (plain string normalises to a
1-element vec, tuple collects into N elements). Mirrors the
`null_as_default` precedent in kvstore.

Adds 5 unit tests for the deserializer plus a wiremock regression that
replays the exact body shape from the bug report. Updates examples and
READMEs across all four languages to reflect the array shape.

Also adds a CLAUDE.md rule to keep Linear/PR identifiers out of code
comments, and removes the 4 pre-existing DX-#### references that had
crept into wire-inspection regression comments.
@johnpmitsch johnpmitsch changed the title Fix DX-5356 + classify 5 deferred bugs as server-side Fix DX-5356 + DX-5421, classify 5 deferred bugs as server-side May 19, 2026
@johnpmitsch johnpmitsch merged commit 88172ca into main May 20, 2026
5 checks passed
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