Preprod indexer ~23h behind chain — wallet ctime stuck at stale block.timestamp causes Custom error 171 (OutOfDustValidityWindow) for all submissions

What we observed

indexer.preprod.midnight.network is currently ~23 hours behind the chain.
This causes all dust spend transactions to fail with Custom error 171
(OutOfDustValidityWindow), because the wallet SDK pulls
blockData.timestamp from the indexer and uses it as DustActions.ctime.

Numbers (captured 2026-05-18 04:19 UTC)

  • chain RPC chain_getBlock height: 828,592 @ 2026-05-18T04:19:12.000Z
  • indexer GraphQL block.timestamp: 814,809 @ 2026-05-17T05:20:54.001Z
  • WSL Date.now(): 2026-05-18T04:19:13.108Z (NTP-synced, +1.1s vs chain)
  • indexer lag: -13,783 blocks ≈ 22.97 hours behind
  • WSL ↔ indexer delta: +82,699,910 ms (~23h)

Root cause trace

In @midnight-ntwrk/wallet-sdk-dust-wallet v1,
RunningV1Variant.js:84 does:

Effect.let('currentTime', ({ blockData }) => currentTime ?? blockData.timestamp)

Callers (e.g. WalletFacade.balanceUnboundTransaction) don’t expose
currentTime as an option, so the fallback blockData.timestamp is
always used. When the indexer is stale, this becomes
tblock - 23h and triggers:

// midnight-ledger/ledger/src/dust.rs (ledger-8)
if self.ctime > tblock || self.ctime + params.dust.dust_grace_period < tblock {
    Err(MalformedTransaction::OutOfDustValidityWindow { ... })
}

with dust_grace_period (per nel349’s earlier post: ~1-3h) far exceeded.

Impact

  • Every new transaction rejected with 1010: Invalid Transaction: Custom error: 171
  • Wallet reports isSynced=true (synced to indexer tip, not chain tip) so
    the failure mode is silent until submission
  • Failure persists indefinitely until indexer catches up

Repro

chain RPC returns the latest block normally. indexer GraphQL block { timestamp height } returns a value ~23h behind. Submitting any
dust-spending tx fails with 171.

Questions

  1. Is this a known Preprod indexer maintenance state, or unexpected?
  2. ETA for indexer to catch up?
  3. Recommended workaround for production-like services that rely on
    Preprod for staging (besides waiting)?
  4. Would it be reasonable to expose currentTime as a
    balanceUnboundTransaction option so callers can override with
    Date.now() when they know the indexer is behind?

Environment

  • Network: Preprod
  • SDKs: @midnight-ntwrk/wallet-sdk-* v1 (ledger-v8 compatible)
  • Node: v22.x
  • Indexer endpoint: https://indexer.preprod.midnight.network/api/v4/graphql
  • RPC endpoint: https://rpc.preprod.midnight.network

Takuya, great catch. Tracing that fallback in @midnight-ntwrk/wallet-sdk-dust-wallet v1 (RunningV1Variant.js:84) all the way down to the OutOfDustValidityWindow ledger condition was seriously impressive debugging work.

Quick status update + a couple workaround options:

  • Is this expected? No definitely not. The public Preprod indexer is currently lagging pretty far behind the actual chain tip. That’s also why the public Faucet has been failing to submit test tokens.
    Your wallet reports isSynced=true because it’s synced to the indexer’s stale tip, not the live ledger tip.

  • ETA: Infra team is already working on it.

  • Workarounds: Since the public Preprod RPC is healthy and fully synced, you can work around the indexer lag in staging a couple ways:

    1. Run a local indexer
      Run your own local indexer in Docker and point it at the public RPC (https://rpc.preprod.midnight.network). Since it syncs against the real chain tip, the SDK gets the correct timestamps and avoids the 171 errors.

    2. Intercept the GraphQL response
      If you want a quicker code-side workaround, you can proxy/mock the indexer provider client’s GraphQL response. When the SDK requests the block timestamp, override it with something like Math.floor(Date.now() / 1000) (or another NTP-synced source) before passing it through.

  • On exposing currentTime in the SDK:
    i have escalated this to the team

Really appreciate the detailed write-up and stack trace that helped narrow this down a lot faster.

1 Like

Confirmed working! Looks like your infra team fixed the indexer on their end — it came back in sync automatically. No workaround needed. New catch report transaction submitted successfully with no errors. Thanks for the quick response!

Glad to hear it Takuya! Thanks for confirming and for the incredibly detailed write-up