Agave v4.0 is out and it’s not a quiet release. UDP is dead, legacy blockstore formats are gone, and a few RPC behaviours changed in ways that will silently break your code if you’re not paying attention.

TL;DR — v4.0 in 60 seconds
getSignaturesForAddressnow errors (not empty array) whenbefore/untilsig isn’t found.- UDP ingestion on the TPU is gone for good — QUIC only.
- Geyser: closed accounts no longer have fields zeroed manually — read the actual on-chain state.
- Massive legacy blockstore format cleanup — irrelevant if you’re running a recent node.
- Program devs:
--debugbuild output path changed. - Validator ops: Linux capability handling is now strict — update your systemd config.
- External scheduler IPC interface is here — niche but powerful.
The RPC changes that will break your code
getSignaturesForAddress now throws on unknown cursors
If you pass a before or until signature that the node doesn’t know about, v4.0 returns a JSON-RPC error with code -32020 instead of a successful response with an empty array.
This silently breaks pagination loops that relied on an empty result as the “you’ve reached the end” signal. Wrap your calls in error handling and check for -32020 explicitly.
// v3 behaviour — safe fallback, empty array on unknown sig
// v4 behaviour — throws RpcError -32020
try {
const sigs = await connection.getSignaturesForAddress(pubkey, {
before: lastKnownSig,
});
} catch (err) {
if (err?.code === -32020) {
// cursor sig not found — handle gracefully
}
}
getClusterNodes now includes a clientId field
Each node object in getClusterNodes responses now has a clientId field identifying the validator software (e.g. Agave, Firedancer). Useful if you’re building tooling that needs to track client diversity across the network.
TPU address CLI args now specify QUIC ports
--public-tpu-address and --public-tpu-forwards-address, along with the corresponding RPC setter methods, now explicitly refer to QUIC ports — not the old UDP ports. If you’re running a custom TPU proxy or forwarding setup, double-check your port assignments.
UDP is officially dead – QUIC only from here
Transaction ingestion over UDP has been removed
This one has been coming for a long time. The --tpu-disable-quic and --tpu-enable-udp flags are gone. The validator no longer accepts transactions over UDP at all — every tx submission path must go through QUIC now.
If you’re using any custom low-level transaction submission code (raw UDP sockets, custom bot infrastructure) that bypasses the standard QUIC flow, you need to migrate. Standard SDK clients and libraries already use QUIC, so most dApp devs are unaffected.
Geyser: closed account notifications changed
Closed accounts no longer have fields zeroed out by Agave
Previously, when an account was closed (balance drained to zero), the Geyser plugin would receive a notification where owner, data, and other fields were manually zeroed out by the validator runtime — even if the on-chain program didn’t do that zeroing itself.
Now, those fields reflect the actual post-transaction state of the account. If the program zeroed them out, they’ll be zero. If it didn’t, they won’t be. This is more correct behaviour, but it means any indexer logic that relied on the old “zero-on-close” shortcut may now see unexpected non-zero data in closed account notifications.
Audit your Geyser consumers: if you use owner == SystemProgram or check for empty data to detect account closures, you may need to rely on the lamport balance instead.
cargo-build-sbf debug output path changed
–debug now outputs to a different location
Two path changes in the platform tools that will break CI scripts or Makefiles that reference the old paths:
• cargo-build-sbf --debug now generates program.so.debug instead of program.debug
• All debug-related artifacts are now placed under target/deploy/debug/ instead of the root deploy directory
# Before v4.0 target/deploy/program.debug # v4.0 and later target/deploy/debug/program.so.debug
Update any CI steps, test scripts, or debug tooling that references these paths.
Things validator operators must action
Linux capability handling is now strict — update systemd
If you run a validator with XDP enabled, v4.0 is no longer lenient about capabilities. Previously a missing capability would log a warning. Now it’s a hard error and the process exits with exit(1).
Update your systemd service file to include these lines in the [Service] section:
# Permit Linux Capabilities required to configure XDP AmbientCapabilities=CAP_NET_RAW CAP_NET_ADMIN CAP_BPF CAP_PERFMON CapabilityBoundingSet=CAP_NET_RAW CAP_NET_ADMIN CAP_BPF CAP_PERFMON
Alternatively, set capabilities directly on the binary (must be repeated on every binary update):
sudo setcap cap_net_raw,cap_net_admin,cap_bpf,cap_perfmon=p /path/to/agave-validator
agave-validator exit now saves bank state
Graceful exits via agave-validator exit now persist the bank state before shutting down. This means if you’ve disabled snapshot generation (--no-snapshots), restarts can still load from local state without needing to download a snapshot from the network.
This is a meaningful improvement for operators running validators in bandwidth-constrained environments or those that want faster restart times.
Snapshot unpacking now uses direct I/O by default
Unpacking snapshot archives now bypasses the OS page cache via O_DIRECT. This avoids polluting the page cache with large snapshot files, which can interfere with other I/O on the same machine. Most modern filesystems (ext4, XFS, ZFS with appropriate settings) support this.
If your filesystem doesn’t support O_DIRECT, pass --no-accounts-db-snapshots-direct-io to fall back to the old behaviour.
External scheduler IPC interface
Passing --enable-scheduler-bindings now exposes an IPC server at <ledger-path>/scheduler_bindings.ipc. This allows external processes to connect and act as schedulers, separate from the validator’s internal scheduling logic.
Niche right now, but opens the door for custom scheduling strategies without forking the validator binary.
Accounts index memory changes and deprecations
The --enable-accounts-disk-index flag is now deprecated — use --accounts-index-limit instead. To match the old behaviour exactly, pass --accounts-index-limit minimal.
Also: using mmap for --accounts-db-access-storages-method is deprecated. The default is already file — you’re likely unaffected unless you explicitly set it to mmap.
Legacy blockstore formats fully removed
Three legacy data formats have been removed from the blockstore. If you’re running an up-to-date node you’ve been writing the new formats for a while, so this shouldn’t affect you — but if you’re operating historical archive nodes or doing ledger forensics on old data, be aware:
• PerfSampleV1 (superseded in v1.15) — gone
• Old TransactionStatus/TransactionMemos/AddressSignatures key format (superseded in v1.18) — gone
• SlotMetaV1 and IndexV1 (superseded in v2.2/v3.1) — gone
The validator will no longer fall back to reading these old column formats at all.
Agave Unstable API — symbols are now private
Internal crates outside the compatibility policy are now gated
All Agave monorepo crates that fall outside the official backwards compatibility policy now have their symbols marked private by default. To use them, you need to explicitly opt-in by enabling the agave-unstable-api crate feature.
This is a signal, not just a compile error: if you’re enabling that feature, you’re acknowledging that the interface can change at any time without warning. This was already the reality — now it’s enforced.
# Cargo.toml
[dependencies]
agave-some-internal-crate = { version = "4.0", features = ["agave-unstable-api"] }
Quick hits — smaller changes worth knowing
Trezor hardware wallet support in the CLI
The Solana CLI now supports Trezor hardware wallets via usb://trezor. Useful for teams that manage validator keypairs or treasury keys on hardware.
blockstore-processor block verification method removed
--block-verification-method blockstore-processor is gone. If you’re explicitly setting this flag, remove it or switch to unified-scheduler. The unified scheduler has been the default for a while now.
Gossip ContactInfo Version struct changed
The Version struct in gossip now encodes semver prerelease status (alpha/beta/rc) in the top two bits of the minor field. If you’re parsing raw gossip messages and reading version info from peer ContactInfo, update your parsing logic accordingly.
Resources
Navigating Agave v4.0? These resources will help:
- 📄 Agave v4.0 Release Notes — Official migration details from the Anza team.
- ⚡ Yellowstone gRPC — Our gRPC streaming service, built for exactly these kinds of protocol shifts. Explore the docs or Learn More.
- 🐇 Rabbitstream — Fastest Solana data streaming solution (pre-processed). Explore the docs or Learn More.
