Agave 4.4.0 shipped as the stable release on 28 October. For the weeks it takes operators to upgrade, an RPC endpoint behind a load balancer is a mix of 4.3 and 4.4 nodes, and each request can land on either. Most of what changed in 4.4 is a new parameter, and a 4.3 node does not reject a parameter it does not know. It ignores it.
The release’s CHANGELOG lists four RPC changes. Diffing the RPC crates from v4.3.2 to v4.4.0 finds six behavior changes that reach clients. One parameter and one notification fix are not in the changelog, and two other RPC fixes on the 4.4 line were backported to 4.3.x, so they are not differences at all. For each change below: what a 4.4.0 node returns, what a 4.3.2 node returns for the same request, and what to send so both give you a usable answer.
Six changes, and what a 4.3 node does with each
The 4.3 column is not a guess. The request-config structs below are copied verbatim from rpc-client-types/src/config.rs at both tags and parsed the way a node parses parameters, with jsonrpc_core::Params::parse into the method’s argument tuple. Neither version sets deny_unknown_fields, so a field the struct does not have is dropped without an error. The 4.4 column was checked against a node running 4.4.0.
getTransaction: null used to mean two things
An indexer that learns of a transaction from one node and fetches it from another has always had a race: the second node may not have reached that slot yet, and getTransaction answers null both for “no such transaction” and for “not yet”. Since #15091, a 4.4.0 node takes minContextSlot and fails fast when it is behind:
→ ["<signature>", { "commitment": "finalized",
"maxSupportedTransactionVersion": 0,
"minContextSlot": 999999999999 }]
← { "error": { "code": -32016,
"message": "Minimum context slot has not been reached",
"data": { "contextSlot": <the node's finalized slot> } } }A 4.3.2 node parses the same request, keeps encoding, commitment and maxSupportedTransactionVersion, and drops minContextSlot, because its config struct has no such field. It answers null exactly as before. A client that added minContextSlot and now treats null as “definitely not there” is wrong whenever a 4.3 node answers.
getSignatureStatuses: the config that fails on 4.3
The same pull request gave getSignatureStatuses a commitment and a minContextSlot, and made searchTransactionHistory optional. That last part is what bites. In 4.3.2 the config struct’s only field had no default, so the natural 4.4-style request is a parse error on a 4.3 node:
→ [["<signature>"], { "commitment": "finalized" }]
4.4.0 ← { "result": { "context": { "apiVersion": "4.4.0", "slot": … },
"value": [{ "confirmationStatus": "finalized", … }] } }
4.3.2 ← { "error": { "code": -32602,
"message": "Invalid params: missing field `searchTransactionHistory`." } }Add "searchTransactionHistory": false and the request parses on both, but a 4.3 node then drops commitment and minContextSlot and answers from its processed bank, which is the only bank this method used before 4.4. On 4.4 without a commitment it still defaults to processed, so leaving the parameter out gives the same answer on both versions.
Subscriptions: dataSlice now does something
accountSubscribe and programSubscribe have accepted a dataSlice for a long time, because they share the account-config type with getAccountInfo. In 4.3.2 the notifier passed no slice to the encoder, so the full account went out regardless. Since #15241 the slice is applied. Subscribing to the Clock sysvar on a 4.4.0 node:
A 4.3.2 node sends 40 bytes for all three. That makes this the one change that can break a client that changed nothing: a subscription config copied from a getProgramAccounts call, slice included, used to stream full accounts and now streams the slice. If you set dataSlice on a subscription, check the length of what arrives, because the answer depends on the node.
getLeaderSchedule: a new error and a new key
Filtering the schedule by an identity that has no slots this epoch used to return {}. Since #12197 it is an error with a code that did not exist before:
→ [null, { "identity": "11111111111111111111111111111112" }]
← { "error": { "code": -32022,
"message": "Node 11111111111111111111111111111112 was not in the leader schedule for specified epoch" } }Code that reads an empty map as “not a leader this epoch” now throws on 4.4 nodes, and code written for the error never sees it from a 4.3 node. Handle both.
The second change is missing from the changelog. #13796 added keyByVoteAccount, which keys the schedule by vote account instead of validator identity, while the identity filter keeps matching on identity. On a 4.4.0 node, a validator’s leader slots come back unchanged under its vote account, the one getVoteAccounts lists for that identity. A 4.3.2 node drops the flag and returns the identity-keyed map. Both are valid base-58 public keys, so nothing fails: the keys are simply not the ones the client asked for.
Confirmed notifications that never came
The last change is a fix, and it is also not in the changelog. When a slot’s confirmation arrived before its bank finished replaying, the node parked it until the bank froze. If the next slot’s confirmation was processed first, the node’s “last notified” mark moved past the parked slot, and the deferred notification emitted nothing. The slot was later announced rooted without ever being announced confirmed, to confirmed WebSocket subscribers and on the Geyser slot-status stream, and its prioritization-fee entry was never finalized.
The pull request reports this “several times per day per node at mainnet volume” on the 4.2 line. 4.4.0 delivers the parked notification, and a follow-up keeps it from being delivered twice. Neither was backported to 4.3. A consumer that waits for “confirmed” before it will accept “rooted” for a slot can stall on 4.3 nodes; treat rooted as implying confirmed.
Writing requests that work on both
- Always send
searchTransactionHistoryin agetSignatureStatusesconfig. It costs nothing on 4.4 and is required on 4.3. - Check
context.apiVersionbefore trusting a new parameter. Every HTTP method that returns a context reports the answering node’s version, and behind a load balancer it can change from one request to the next. An answer from a 4.3 node was given without yourcommitmentorminContextSlot. - Keep retrying a
nullfromgetTransactionwhen you know the transaction exists.getTransactionreturns no context, so a 4.3 answer cannot be told apart; on 4.4, retry -32016 until the node’scontextSlotcatches up. - Treat
{}and -32022 as the same answer, and verify vote-account keys againstgetVoteAccountsbefore relying onkeyByVoteAccount. - Measure subscription payloads if you pass
dataSlice, and drop it if you were relying on the full account.
For Rust clients, the 4.4 solana-rpc-client adds get_signature_statuses_with_config, and #15309 lets get_fee_for_message take a v1 message. One more 4.4 change, a bound on the root scan in getBlocks, makes the method faster without changing what it returns.
What this does not show
The 4.3 behavior is reproduced from its source, by parsing requests with the exact structs a 4.3.2 node uses, not by sending them to a 4.3.2 node. How long mainnet stays mixed, and how a given provider routes requests, are not measured here. Rejecting base58 encoding for v1 transactions and the camelCase fields of getAgGenesisCert were both backported to 4.3.x, so they are left out as changes between these two versions.
Agave 4.4 RPC: Questions People Actually Ask
Does getTransaction support minContextSlot?
From Agave 4.4.0, yes. A node whose context slot at the requested commitment is below the minimum returns error -32016 with the node’s contextSlot in the error data, instead of null. A 4.3 node accepts the parameter and ignores it, so it can still return null for a transaction it simply has not seen yet.
Why does getSignatureStatuses fail with “missing field searchTransactionHistory”?
The node is running 4.3 or earlier. Before 4.4.0 the config object’s searchTransactionHistory field had no default, so a config that only sets commitment or minContextSlot fails to parse with error -32602. Always send searchTransactionHistory explicitly and the request parses on both versions.
What is RPC error -32022?
New in Agave 4.4.0: getLeaderSchedule returns -32022 when the identity you filter by is not in the leader schedule for the epoch. Earlier versions returned an empty object for the same request.
Does accountSubscribe support dataSlice?
From Agave 4.4.0, accountSubscribe and programSubscribe apply dataSlice to binary encodings, and a zero-length slice returns empty data. Earlier versions accepted dataSlice on subscriptions and ignored it, always sending full account data.
How can a client tell which version answered?
HTTP methods that return a context object include context.apiVersion, the answering node’s version; WebSocket notifications do not. Behind a load balancer each request can reach a different node, so check it per response, not once per connection.
The one-line version
Agave 4.4 mostly adds parameters, and 4.3 nodes ignore parameters they do not know. Until the fleet behind your endpoint has upgraded, the new protections are only as good as the version that answered, so read apiVersion and write requests that are valid on both.
The full list of custom error codes, which -32022 now extends, is in Solana RPC error codes explained, and what each commitment level means is in how many confirmations a deposit needs.
Sources: the v4.4.0 release and its CHANGELOG; rpc-client-types/src/config.rs at v4.3.2 and v4.4.0; rpc/src/rpc_subscriptions.rs at v4.3.2 and v4.4.0; and pull requests #12197, #13796, #14936, #15068, #15091, #15131, #15241 and #15309 in anza-xyz/agave.