Sepolia: handles created after ~2026-08-31 will not decrypt, while older handles on the same contract still do

Edit: Back up now ~6:15pm est. My test suite passes now and the new handles properly decrypt, no code changes on my end. The old failing ones are working properly now.

Old post:
Since some point after 2026-08-30 I cannot decrypt any newly created handle on Sepolia, while older handles on the same contract, same account and same code decrypt normally. I have a clean control on both sides and I have not been able to make it my own bug, so I think it may be infrastructure, but I would rather be told I am wrong.

Symptom

User decryption: Relayer API error [internal_server_error]:
Transaction simulation failed: Execution reverted: execution reverted

status: 500
url: https://relayer.testnet.zama.org/v2/user-decrypt
operation: USER_DECRYPT

Reproduction, with a control

One account, one contract (an OpenZeppelin ERC7984ERC20Wrapper), two balance handles. The
only difference between them is when they were minted.

handle A  0xc2f25cb3d62b950dc2c58e0bba0aec056b0ed5e5c2ff0000000000aa36a70500
          minted 2026-08-30T02:01:36Z (block 11595767)
          -> decrypts fine, repeatedly, today

handle B  0x6bcd1048d904ae7a1991aecdc7d1592bdcaff43ecfff0000000000aa36a70500
          minted 2026-09-01T19:18:12Z (block 11614598)
          -> has never decrypted; still failing 45+ minutes after minting

Handle A was decrypted successfully on its first ever attempt, seconds after it was minted, on 2026-08-30. So the same code path did work on a brand-new handle two days earlier, and handle A’s success today cannot be a cached result from some earlier decryption.

I also polled handle B for 25 minutes. It never succeeded.

It is not only us — other people’s contracts are affected right now

This is the part I would most like someone to check, because it means the problem is wider
than one project.

I took recent AllowedForDecryption events straight from the ACL registry at
0xf0Ffdc93b7E186bC2f8CB3dAA75D86d1930A433D, filtered to a contract that has nothing to do
with us
0x290df9be25eafcc26d6c5e6c2ea8034113ccfee0 — and tried to publicly decrypt the
handles it had just published. No permissions needed; the contract had already marked them
public itself.

handle 0x62f3c08ef2ee0206ff774f667e12b0cc729ea65918ff0000000000aa36a70500   1 min old   FAILS
handle 0x15b74a6cfbb914e96beaf75f0331dcce6efc279a12ff0000000000aa36a70500   1 min old   FAILS
handle 0x07ab5550118d3506d56e55b037a1e39c9164e8c646ff0000000000aa36a70500   2 min old   FAILS
handle 0x58aefab90dc7ebba789bd23d45de45422630e83cadff0000000000aa36a70500   3 min old   FAILS

All four report isAllowedForDecryption == true on-chain. All four fail with the same
Transaction simulation failed: Execution reverted.

That contract is still writing new handles as I post this, so whoever runs it is affected too
and may not have noticed yet.

It is not SDK-specific

Both SDKs, both flows, same account, same two handles:

@zama-fhe/relayer-sdk 0.4.4     handle A  OK        handle B  FAIL
@zama-fhe/sdk 3.4.0 (v2 flow)   handle A  OK (3.5s) handle B  FAIL

@zama-fhe/sdk classifies the failure as retryable: false.

It is not one contract or one decryption path

A second person reproduced the same split independently, using publicDecrypt on one contract and userDecrypt on another - three unrelated contracts in total, both mdecryption paths, all showing the same “old works, new does not” boundary.

Reproduction: one account, one session, three minutes apart

This is the tightest version, and it needs no comparison across days or accounts.

Account 0x9B25F1f746147796DeE1A11637CcA8770e0A73E0, one pool contract, one EIP-712 permit,
nothing else touched:

1. decrypt my confidential share          -> OK
      handle 0x92dcd3ab20b47e0c40cb00ffc15462aa8aa541d7dbff0000000000aa36a70500

2. deposit into the pool                  -> OK, mined 2026-09-01T20:36:36Z, block 11614978
      the deposit updates my share, which necessarily produces a NEW handle
      handle 0xbde263d033db61abacf8eff9df5a3a066ece79206fff0000000000aa36a70500

3. decrypt my confidential share again    -> FAILS, and has not recovered

The account did not change, the contract did not change, the permit did not change, and the
old handle had been decrypting happily moments earlier. The only thing that changed is that
step 2 created a new handle.
Both handles report persistAllowed == true for this account.

So an account is not “good” or “bad” — it becomes affected the instant it writes a new value.

What is correct on-chain at the moment of failure

ACL.persistAllowed(handleB, owner)     true
ACL.persistAllowed(handleB, token)     true
confidentialBalanceOf(owner)           == handle B   (it is the current handle, not a stale one)

An established public handle on an unrelated contract also decrypts throughout, in about
2.4 s, so the relayer and the KMS are clearly up and answering:

publicDecrypt(0xbff29d0ca71b95104aa88c7c6237e6e1976ed5efb7ff0000000000aa36a70500) -> 650

What we checked on our own side first

Before concluding anything, in case something of ours changed in the same window:

  • Our contracts cannot have changed. All three are plain and immutable — no EIP-1967
    implementation, admin or beacon slots are set, so there is no upgrade path. The pool’s
    deployed bytecode is byte-for-byte the length we recorded on 2026-08-23. On 2026-08-30 its
    handles decrypted immediately; today they do not. Identical bytecode cannot change its own
    behaviour.
  • Nothing was redeployed. No .sol and no deployment manifest has changed in our
    repository since 2026-08-29; the only edits are to off-chain scripts.
  • It is not our client. Two SDKs sharing none of the same client code behave identically.
  • It is not this machine. Its clock is within 10 s of chain time, and older handles decrypt
    from the same machine, the same network and the same TLS session, minutes apart from the
    failures.
  • It is not our own load. Our only automation funds once per draw and otherwise exits in
    about two seconds.
  • KMS key material initializes. /v2/keyurl answers 200, and the KMS contract returns a
    signer set and a threshold of 7.

Ruled out

  • Permissions - persistAllowed is true for both the owner and the token.
  • A stale handle - the failing handle is the one confidentialBalanceOf currently returns.
  • Address checksumming - all addresses are EIP-55 checksummed. (Passing a lowercase
    address gives a different, client-side Bad address checksum from the KMS WASM; this is not that.)
  • Permit validity window - startTimestamp was 150 s behind chain time; our clock is
    within 10 s of the chain.
  • Signer/user mismatch - that produces “User address … is not authorized”, not this.
  • A general outage - established handles decrypt throughout, on this contract and others.
  • SDK version or the legacy/v2 split - both fail identically.

Environment

chain     Sepolia (11155111)
tokens    OpenZeppelin ERC7984ERC20Wrapper over a 6-decimal ERC-20
SDKs      @zama-fhe/relayer-sdk 0.4.4 and @zama-fhe/sdk 3.4.0

The question

Is this expected behaviour that I should be designing around - a coprocessor or Gateway replication window that can extend to tens of minutes - or is something stuck on the Sepolia testnet since roughly 2026-08-31?

I ask because the two have opposite consequences for my application. If it is a window, an app has to show “not readable yet” and retry, and I would love to know a realistic upper bound so the copy can be honest. If something is stuck, no amount of client-side patience helps.

For what it is worth, the practical impact on our app is not just balances: freezing a value for a scheduled task creates a new handle too, so anything that snapshots-then-decrypts stalls in the same way.

Happy to supply transaction hashes, a minimal repro script, or to re-run anything against a specific handle.

Hi there, thanks for flagging this.

Thank you for the exceptionally detailed report and controls. They helped us correlate the behaviour with our infrastructure logs.

This was a transient issue affecting the Sepolia testnet, not an expected propagation window that applications should need to design around. Our initial investigation points to stale RPC data being used by part of the relayer infrastructure, which caused decryption simulations for newly created handles to fail while older handles remained decryptable.

The service has now recovered and, as you observed, the affected handles decrypt again without any client or contract changes. We’re following up internally to confirm the root cause and review our monitoring and alerting.

Apologies for the disruption, and thanks again for the thorough investigation.

I am facing a similar issue with decryption, that suddenly started happening today.
I am seeing a newly created Sepolia handle fail consistently:

Current cToken balance handle:
0xc96af1631eadd0f643a0f58914eabc659e23650c7cff0000000000aa36a70500

Created/updated:
2026-09-03T00:15:00Z
Block: 11623016

Transaction:
0x9764d06ec548f6985db8fcccbc8392453b3ac52ebf668c65cf3e2dcb956ccf4e

The exact client error is:

DecryptionFailedError: Failed to decrypt encrypted values
[code=DECRYPTION_FAILED, retryable=false]

Error reconstructing all blocks:
Gao decoding failure:
Allowed at most 0 errors but xgcd factor degree indicates 1..
n=13, deg=4, #shares=9, block_shares=9, recovery_errors=0

The relayer and KMS are reachable and return nine shares, but one appears inconsistent. Since nine is the minimum reconstruction quorum, the client cannot recover the plaintext.

Used:

  • @zama-fhe/relayer-sdk legacy user decryption
  • @zama-fhe/sdk@3.5.1
  • @zama-fhe/sdk@3.6.0-alpha.1 with sdk@0.14.1-0
  • Fresh transport keys and permits
  • Batched decryption
  • Single-handle decryption
  • Multiple retries
  • ACL permissions for the wallet and owning contract

All produce the same reconstruction failure. The encrypted balance and ACL state are correct on-chain.

This account also has an older deposited-principal handle created on 2026-08-29, before the boundary discussed above, and the account had decrypted successfully previously. The newly updated wallet-balance handle is the one currently failing.

Since the earlier incident was reported as recovered on September 2, could this be a recurrence of stale or inconsistent relayer/KMS context data for handles created afterward? Happy to provide the contract address, complete logs, or run another reproduction if useful.