Repository navigation
[BUG] PXE storePendingIndexes throws "Conflicting range" — execution-time and sync-time paths disagree on lowestIndex #23574
Description
What are you trying to do?
Reading a private note from a PXE after submitting a transaction that delivers notes via ONCHAIN_CONSTRAINED. During note-discovery sync, the PXE consistently aborts with a Conflicting range error:
Error: Conflicting range for secret 0x096f3e58...:0x174f56ff... and txHash 0x196bd441...:
existing [0, 5] vs new [1, 5]
Thrown from yarn-project/pxe/src/storage/tagging_store/sender_tagging_store.ts (storePendingIndexes).
Code Reference
A minimal standalone repro is hard to extract, so here is the source-level root cause instead. Two code paths write a pending range for the same (secret, txHash) pair but compute lowestIndex with different semantics:
1. Execution-time path — yarn-project/pxe/src/pxe.ts stores taggingIndexRangesUsedInTheTx after proving:
// pxe.ts (~L788)
const taggingIndexRangesUsedInTheTx = privateExecutionResult.entrypoint.taggingIndexRanges;
if (taggingIndexRangesUsedInTheTx.length > 0) {
const txHash = (await txProvingResult.toTx()).txHash;
await this.senderTaggingStore.storePendingIndexes(taggingIndexRangesUsedInTheTx, txHash, jobId);
}The range here comes from contract_function_simulator/execution_tagging_index_cache.ts, where lowestIndex is initialized to the true first index the tx used:
// execution_tagging_index_cache.ts (~L23)
this.taggingIndexMap.set(secret.toString(), { lowestIndex: index, highestIndex: index });For a tx that tags logs with indexes 0..5 on one directional secret, this stores [0, 5].
2. Sync-time path — tagging/sender_sync/utils/load_and_store_new_tagging_indexes.ts:
// load_and_store_new_tagging_indexes.ts (~L43)
const ranges = [{ extendedSecret, lowestIndex: Math.min(...indexes), highestIndex: Math.max(...indexes) }];
await taggingStore.storePendingIndexes(ranges, txHash, jobId);Here indexes are only those found within the current scan window [start, end). The window start is anchored to finalizedIndex + 1 (enforced in storePendingIndexes: lowestIndex must be > finalizedIndex). Once index 0 has been finalized, the window starts at 1, the scan never observes index 0, and it recomputes [1, 5].
storePendingIndexes then finds the existing [0, 5] entry for that pair, sees a different range, and throws:
// sender_tagging_store.ts (~L192)
if (existingEntry.lowestIndex !== range.lowestIndex || existingEntry.highestIndex !== range.highestIndex) {
throw new Error(
`Conflicting range for secret ${secretStr} and txHash ${txHashStr}: ` +
`existing [${existingEntry.lowestIndex}, ${existingEntry.highestIndex}] vs ` +
`new [${range.lowestIndex}, ${range.highestIndex}]`,
);
}Why this appears to be a bug rather than a real inconsistency: lowestIndex is not used by note discovery. The rest of the store keys off highestIndex — see getTxHashesOfPendingIndexes ("we check based on the highest indexes only") and the pruning in finalizePendingIndexes which filters on item.highestIndex > currentFinalized. So the assertion aborts sync over a discrepancy in a field that does not affect correctness.
Triggering conditions (in lieu of a minimal repro): a transaction that emits multiple tagged logs under a single directional app tagging secret (so the tx occupies a multi-index range such as [0, 5]), where the lower indexes become finalized between the execution-time store and a later sync-time re-scan. In a local sandbox the block/finalization timing is deterministic, so this reproduces on every run.
Suggested fix: reconcile instead of throwing — when an entry for (secret, txHash) already exists, retain min(existing.lowestIndex, new.lowestIndex) and max(existing.highestIndex, new.highestIndex); or validate/key only on highestIndex, since that is the field discovery actually relies on.
Aztec Version
v4.2.0-aztecnr-rc.2
OS
Linux (6.8.0)
Node Version
v22.14.0
Additional Context
Line numbers above are from the v4.2.0-aztecnr-rc.2 tag and may differ on next. The error is fully reproducible in a local sandbox; it surfaced when reading a PrivateMutable note via a utility view after the note was delivered ONCHAIN_CONSTRAINED in a prior transaction.
Activity
nventuro commentedon May 27, 2026
This has all the signs of being a duplicate of #22949, which was fixed in #23044 and released as part of
v4.3.0. You should be able to simply upgrade the js stack to this version (i.e. the wallet/pxe) and continue using contracts compiled and deployed withv4.2.0with no problem whatsoever - PXE changes are backwards compatible.