Fix 7 SDK bugs from DX-5040 sweep#20
Conversation
Sweeps SDK-side fixes from the sdk-smoke-tester bug series (DX-5340 → DX-5361). Server-side / wire-inspection bugs are deferred to follow-ups. - DX-5340 (0001): kvstore getSets/getLists now tolerate `data: null` via a null-as-default deserializer; previously threw DecodeError on an empty store. - DX-5345 (0006): streams WebhookAttributes.compression is now Option<String> with skip_serializing_if so the field is omitted instead of sent as "". - DX-5354 (0015): CreateStreamParams.dataset_batch_size and elastic_batch_enabled are required (matches the API contract). - DX-5355 (0016): CreateStreamParams.plan and threshold_fetch_buffer are optional (the API doesn't require either). - DX-5359 (0020): pruned 5 phantom destinations (mysql, mongo, clickhouse, snowflake, redis) the API and UI don't support — keeps webhook, s3, azure, postgres, kafka. - DX-5361 (0021, dup DX-5360): renamed Kafka max_request_size → max_message_bytes to match the API field. - DX-5350 (0011) + DX-5357 (0018): documented previously-undocumented constraints in doc-comments (tag label ≤25 chars; webhook max_retry 1–10; security_token ≥32 bytes; postgres sslmode disable|require). All changes mirrored across core, python/node/ruby bindings, RBS sigs, sdk.d.ts, and init_manual_override.pyi. Added regression tests for 0001.
DX-5360 SDK Bug 0021 — streams: Kafka destination's `maxRequestSize` field doesn't map to the API's `max_message_bytes`
Symptom
When The API uses the field name Reproductionawait qn.streams.createStream({
/* ... */
destinationAttributes: {
destination: "kafka",
attributes: {
bootstrapServers: "kafka.example.invalid:9092",
topicName: "smoke",
compressionType: "none",
batchSize: 100,
lingerMs: 100,
maxRequestSize: 1048576, // <-- this never reaches the wire as `max_message_bytes`
timeoutSec: 30,
maxRetry: 3,
retryIntervalSec: 1,
},
},
});
// 400 — "max_message_bytes should be greater than 0"Adding ExpectedEither:
Suspected causeThe SDK type was generated against a Kafka client library's naming convention (rdkafka uses "request size") rather than the Quicknode API's field name. The Rust core or wire-format layer doesn't apply a NotesThis blocks Discovered by the sdk-smoke-tester e2e suite. Bug file: DX-5340 SDK Bug 0001 — kvstore: getSets DecodeError on empty store (`data: null`)
Symptom
Reproductionimport { QuicknodeSdk } from "@quicknode/sdk";
const qn = QuicknodeSdk.fromEnv();
await qn.kvstore.getSets(); // throws DecodeError when store is emptyObservedThe HTTP response is a success (200), and the body is well-formed JSON; the server simply uses Expected
Suspected causeRust core deserializer for NotesThis bug also takes down Discovered by the sdk-smoke-tester e2e suite. Bug file: DX-5345 SDK Bug 0006 — webhooks: compression typed optional but server rejects when omitted
Symptom
Reproductionimport { QuicknodeSdk, TemplateArgs } from "@quicknode/sdk";
const qn = QuicknodeSdk.fromEnv();
await qn.webhooks.createWebhookFromTemplate({
name: "smoke-test",
network: "ethereum-mainnet",
destinationAttributes: { url: "https://example.com" }, // compression omitted
templateArgs: TemplateArgs.evmWalletFilter({
wallets: ["0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48"],
}),
});ObservedIf ExpectedEither:
Suspected causeSDK serializer is converting an absent / NotesThis breaks the entire webhook section of the smoke suite when compression is unspecified — 22+ failing tests trace back to this one bug. Workaround for now: always pass Discovered by the sdk-smoke-tester e2e suite. Bug file: DX-5350 SDK Bug 0011 — admin: tag labels are silently capped at ~25 chars with no documented limit
SymptomThe Reproductionimport { QuicknodeSdk } from "@quicknode/sdk";
const qn = QuicknodeSdk.fromEnv();
const ep = await qn.admin.createEndpoint({ chain: "ethereum", network: "mainnet" });
// 25 chars: OK
await qn.admin.createTag(ep.data.id, { label: "a".repeat(25) });
// 26+ chars: 400
await qn.admin.createTag(ep.data.id, { label: "a".repeat(30) });ObservedThe error message gives no hint that length is the problem. ExpectedEither:
Suspected causeServer-side validation rule that the API surface doesn't communicate clearly. The SDK is forwarding the request faithfully. NotesEmpirically determined boundary:
The same 400 error is also returned for some "could not create" cases unrelated to length (e.g., missing params), so consumers can't distinguish length-rejection from other failures. Discovered by the sdk-smoke-tester e2e suite. Bug file: DX-5354 SDK Bug 0015 — streams: datasetBatchSize and elasticBatchEnabled typed optional but required by the API
Symptom
Reproductionimport { QuicknodeSdk } from "@quicknode/sdk";
const qn = QuicknodeSdk.fromEnv();
await qn.streams.createStream({
name: "smoke-test",
region: "UsaEast",
network: "ethereum-mainnet",
dataset: "Block",
startRange: 24_691_804,
endRange: 24_691_814,
destinationAttributes: {
destination: "webhook",
attributes: {
url: "https://example.com",
maxRetry: 3,
retryIntervalSec: 1,
postTimeoutSec: 10,
compression: "none",
},
},
plan: "growth_plan",
thresholdFetchBuffer: 1000,
status: "Paused",
// datasetBatchSize omitted
// elasticBatchEnabled omitted
});ObservedPassing both fields explicitly (e.g. ExpectedEither:
Suspected causeSDK type definitions are out of sync with the upstream API contract. The Rust core probably treats these as NotesThe README example in Discovered by the sdk-smoke-tester e2e suite. Bug file: DX-5355 SDK Bug 0016 — streams: `plan` and `thresholdFetchBuffer` required by the SDK but absent from the API contract
Symptom
The upstream Reproductionawait qn.streams.createStream({
name: "smoke-test",
region: "UsaEast",
network: "ethereum-mainnet",
dataset: "Block",
startRange: 24_691_804,
endRange: 24_691_814,
datasetBatchSize: 1,
elasticBatchEnabled: true,
destinationAttributes: { destination: "webhook", attributes: { /* ... */ } },
status: "Paused",
// plan and thresholdFetchBuffer omitted
});ObservedThe SDK throws synchronously: (Did not even reach the HTTP layer.) If the API is checked directly via curl without these fields, it accepts the request. ExpectedEither:
Suspected causeLooks like a contract drift: the SDK was built against an older API spec where NotesThe same is likely true for other streams operations —
Discovered by the sdk-smoke-tester e2e suite. Bug file: DX-5357 SDK Bug 0018 — streams: destinations have undocumented field constraints (webhook maxRetry/securityToken, postgres sslmode)
Symptom
Reproduction// max_retry=0
await qn.streams.createStream({
/* ... */
destinationAttributes: {
destination: "webhook",
attributes: {
url: "https://example.com",
maxRetry: 0,
retryIntervalSec: 1,
postTimeoutSec: 10,
compression: "none",
},
},
});
// 400 "max_retry must be in the range of 1 to 10"
// security token < 32 bytes
await qn.streams.createStream({
/* ... */
destinationAttributes: {
destination: "webhook",
attributes: {
url: "https://example.com",
maxRetry: 3,
retryIntervalSec: 1,
postTimeoutSec: 10,
compression: "none",
securityToken: "short-token",
},
},
});
// 400 "security token length is less than the recommended 256 bits (32 bytes)"ExpectedEither:
The server-side error wording for the token says "less than the recommended 256 bits" — using the word "recommended" while the API returns 400 is contradictory. Either it's a hard requirement (then say "required") or it's truly a recommendation (then accept the shorter token with a warning header). NotesAdjacent finding: other numeric fields ( Postgres destination
Empty string yields a separate (clearer) error: Either the doc comment should spell out the supported subset, or the SDK should narrow the type to Discovered by the sdk-smoke-tester e2e suite. Bug file: DX-5359 SDK Bug 0020 — streams: SDK exposes 5 destination types the API and UI don't support
SymptomThe SDK's | { destination: "webhook"; ... }
| { destination: "s3"; ... }
| { destination: "azure"; ... }
| { destination: "postgres"; ... }
| { destination: "mysql"; ... }
| { destination: "mongo"; ... }
| { destination: "clickhouse"; ... }
| { destination: "snowflake"; ... }
| { destination: "kafka"; ... }
| { destination: "redis"; ... };The upstream API supports only five: webhook, s3, postgres, azure, kafka. The UI (Streams "Select Your Destination" picker) also shows only these five. Calling (The error message lists the full server-side enum, confirming the SDK is out of sync.) Reproductionawait qn.streams.createStream({
/* ... */
destinationAttributes: {
destination: "mysql",
attributes: {
host: "mysql.example.invalid", port: 3306, database: "smoke",
username: "smoke", password: "smoke-password-12345",
tableName: "smoke", maxRetry: 3, retryIntervalSec: 1,
},
},
});
// 400 — "Invalid destination. Destination must be one of the following values: webhook, s3, postgres, azure, ..."Same shape for ExpectedEither:
The SDK's interfaces ( NotesThe smoke suite plan originally called for one test file per destination (10 files). With this finding the scope drops to 5: Confirmed: the typed wrapper layer ( Discovered by the sdk-smoke-tester e2e suite. Bug file: DX-5361 SDK Bug 0021 — streams: Kafka destination's `maxRequestSize` field doesn't map to the API's `max_message_bytes`
Symptom
When The API uses the field name Reproductionawait qn.streams.createStream({
/* ... */
destinationAttributes: {
destination: "kafka",
attributes: {
bootstrapServers: "kafka.example.invalid:9092",
topicName: "smoke",
compressionType: "none",
batchSize: 100,
lingerMs: 100,
maxRequestSize: 1048576, // <-- this never reaches the wire as `max_message_bytes`
timeoutSec: 30,
maxRetry: 3,
retryIntervalSec: 1,
},
},
});
// 400 — "max_message_bytes should be greater than 0"Adding ExpectedEither:
Suspected causeThe SDK type was generated against a Kafka client library's naming convention (rdkafka uses "request size") rather than the Quicknode API's field name. The Rust core or wire-format layer doesn't apply a NotesThis blocks Discovered by the sdk-smoke-tester e2e suite. Bug file: |
Summary
SDK-side fixes from the sdk-smoke-tester bug series tracked under DX-5040 (bugs DX-5340 → DX-5361). Server-side / wire-inspection bugs are deferred to follow-up tickets.
Bugs fixed
getSets/getListstoleratedata: nullvia null-as-default deserializer (previously threwDecodeErroron an empty store)WebhookAttributes.compressionisOption<String>withskip_serializing_ifso it's omitted instead of sent as\"\"dataset_batch_sizeandelastic_batch_enabledare now required inCreateStreamParams(matches API)planandthreshold_fetch_bufferare now optional (the API doesn't require either)max_request_size→max_message_bytesto match APImax_retry1–10;security_token≥32 bytes; postgressslmodedisable/requireonly)Polyglot coverage
All public changes mirrored across:
crates/core)crates/{python,node,ruby})npm/sdk.d.ts)python/sdk/init_manual_override.pyi)ruby/sig/quicknode_sdk.rbs)npm/index.d.ts,python/sdk/_core/__init__.pyi) regenerated by the buildBugs deferred to backend / follow-up
Filed as out-of-scope for this SDK sweep — most need wire-inspection or server-side fixes:
Test plan
cargo check --workspacecleanjust lint(clippy-D warnings) cleanjust test— 179 Rust tests pass (down from 184 after removing 5 phantom-destination roundtrips); 2 new regression tests for DX-5340just node-build(build + napi tests) cleanjust ruby-buildcleanjust python-buildclean (stubs regenerated)@quicknode/sdkdep at this branchNote
Medium Risk
Medium risk due to breaking surface-area changes in Streams (required/optional field shifts, removed destination variants, renamed Kafka field) across Rust/Node/Python/Ruby/TS bindings; KVStore changes are low risk but touch response decoding.
Overview
Fixes KVStore empty-store decoding by treating
data: nullas a default empty value forget_sets/get_lists, and adds regression tests for this behavior.Aligns Streams types with the API by making
dataset_batch_sizeandelastic_batch_enabledrequired on create, makingplan/threshold_fetch_bufferoptional, and making webhookcompressionoptional (omitted when unset). Also renames Kafkamax_request_size→max_message_bytesand removes unsupported stream destinations (MySQL/Mongo/Clickhouse/Snowflake/Redis) from core enums and all generated bindings/stubs (Node/TS/Python/Ruby).Reviewed by Cursor Bugbot for commit c609d90. Bugbot is set up for automated code reviews on this repo. Configure here.
Linear
Fixes DX-5340
Fixes DX-5345
Fixes DX-5350
Fixes DX-5354
Fixes DX-5355
Fixes DX-5357
Fixes DX-5359
Fixes DX-5361