Build1 distinct publisher3 min readUpdated
A developer's local-stack write-up traces error 117 and a transaction_merge panic to overwriting an existing ledger key. The fix is append-only writes plus a real membership check.
The Engineer · Build desk
Compiled by The EngineerSomething wrong?How this is made
A developer building a token circuit on Midnight Local Undeployed reported losing an afternoon to `SubmissionError` and `FiberFailure` wrapping `RpcError 1010: Invalid Transaction: Custom error: 117`, plus a WASM panic that surfaced as `Unreachable` in `feesWithMargin` / `transaction_merge` [1]. The circuit compiled, the proof server was up and the genesis wallet held tDUST; the defect was in how the code used a Compact `Map` [2]. That gap between symptom and cause is the whole story, because none of those messages name the ledger operation that caused them.
The rule the author lands on is blunt: on Local Undeployed, do not update an existing `Map` key, insert a new one [3]. He says he verified this against a running local stack rather than reasoning about it [4]. The EVM-shaped version - `lookup` a balance, then `insert` the same key with the decremented value - compiles as valid Compact but does not survive `callTx` on this stack, and updating an existing key is what trips the merge path [11]. He hit the same class of failure twice: first on an NFT `owners` map, then again on a token `balances` map after believing the NFT case was fixed, with the same ledger shape and the same panic [12].
The second half of the post is about privacy plumbing that developers coming from Solidity will read as ceremony. A Compact circuit has three tiers: witnesses such as `localSecretKey()`, circuit-local values including hashes, asserts and arithmetic, and the public ledger; only `disclose(...)` moves a value from private to public [9]. So `credits.insert(pk, amount)` without `disclose` is an attempt to write a private value into public state, and the compiler or runtime rejects it [10]. The signer key `fromPk` is a `persistentHash` over a padded domain tag and the witness secret, which means it is private until disclosed [17].
That is also where the access-control mistake lives. Hashing a witness and then checking the result is not empty proves the hash function ran, not that the caller is entitled to spend; the shipped circuit gates transfers on `faucet_claimed.member(fromPk)` instead [18]. The rewritten `MidnightUSDC.compact` under Compact 0.23 keeps four ledger fields - `credits`, `credit_to`, `faucet_claimed`, `spent_nonces` [14]. The faucet inserts once per signer key and grants 10000000 units [15]. Transfer asserts the nonce is unspent, the amount is non-zero and the signer has claimed, then writes rows keyed by the fresh nonce [16]. The NFT flow follows the same discipline: `owners` insert on mint only, sales appended under a fresh random id, and cancellation writing a new listing id rather than overwriting the old price key [19]. The circuit is called `listSale` because `list` is a Compact keyword [20].
The cost is not hidden. Each transfer writes three new ledger entries, so state grows with transaction count rather than holder count [21], and the author's own advice is to treat the ledger as an append-only journal and reconstruct display balances off-chain [13].
Watch the environment pins, because they change which failure you get. Local Undeployed writes are server-append and Lace does not sign them [5]; `setNetworkId("undeployed")` is a string at midnight-js 4.1.1 [6]; the standalone chain funds the genesis seed ending 0002, not 0001 [7]. Drift there produces a different class of error, often 196 for verifier mismatch, and mixing the public-network proof server 8.1.0 with this indexer is called out specifically [8]. This is one developer's account of one local stack, so the open question is whether the merge-path failure is a bug in Undeployed or the intended semantics of key updates.
Follow any of these and your For You feed starts watching them — no settings page required.
Ranked by verification strength, evidence, and original report placement.
The author chased SubmissionError / FiberFailure wrapping 'RpcError 1010: Invalid Transaction: Custom error: 117' and a WASM panic that looks like feesWithMargin / transaction_merge Unreachable, spending an afternoon on it.
The circuit compiled, the proof server was up, and the genesis wallet had tDUST; the bug was in how the author used Compact Map.
Stated rule: on Midnight Local Undeployed, do not update an existing Map key; insert a new key instead. If a value must become public, disclose() it.
The author says he verified this against a real local stack, not a sketch.
Local Undeployed writes are server-append, and Lace does not sign them.
Evidence-backed comparisons of source perspectives and observed adoption signals. Read the methodology
Which Builder, Operator, and Investor concerns the observed source mix emphasized—not a truth score.
Evidence, demonstrated adoption, hype gap, incentives, and confidence are assessed independently, each on its own current evidence. How these are measured.
Hands-on but single-source and unreproduced
The post supplies unusually concrete artifacts for a single-source story: verbatim error strings, the failing overwrite circuit, the full rewritten MidnightUSDC.compact listing, the NFT append pattern, and named version pins. That makes the code-shape claims easy to check. What is missing is any second party: no upstream Midnight issue, changelog, or independent reproduction confirms that re-inserting an existing Map key is what trips transaction_merge, and the ancillary error-196 rule is asserted without a log. Evidence is therefore detailed but narrow, and the causal step rests on one developer's afternoon.
One developer's local stack
The only adoption signal supplied is a single developer running two of his own projects - a demo USDC token circuit and an NFT list/buy flow - on a local Midnight Undeployed stack with named toolchain pins. There is no deployment to a public network, no user or transaction counts, no other practitioners reporting the same pattern, and no vendor or documentation uptake of the append-only rule. Adoption is real but minimal in scope.
Categorical rule from one afternoon
The framing is mildly overstated relative to what is shown. The post generalises a single debugging session into an unconditional rule - "do not update an existing Map key" on Midnight Undeployed - and into a mechanism ("updating an existing key trips the merge path") that no upstream reference or second run in the cluster confirms; error 117 and a transaction_merge panic are symptoms consistent with several causes, including the private-state/LevelDB mismatch the same post mentions. Offsetting the overstatement, the author is explicit about the local-only scope, publishes the code, and the disclose() and access-control lessons are demonstrated rather than asserted, so the gap is modest rather than large.
Practitioner write-up, reputational upside only
Incentive distortion looks low. The material is a first-person post on a developer-community platform, not vendor marketing: it publishes failures, names version pins, and promotes no paid product, service, or token. The residual pressures are ordinary for the format - reputational credit for solving a hard bug, which rewards a crisp categorical rule over a hedged one, and the author's stake in the demo projects being described as fixed. Nothing in the cluster indicates sponsorship or a Midnight affiliation.
Credible artifacts, unverified mechanism
Confidence is moderate. The descriptive layer is highly reliable - the circuit listings, ledger declarations, disclose() semantics and the weak-auth pattern can be read straight from the post - so claims about what the author shipped are on firm ground. Confidence drops on the explanatory layer: with one publisher, one adopter, no upstream corroboration and version-specific behaviour on a pre-production local network, the diagnosis that key updates cause the transaction_merge panic could be superseded by a toolchain fix or a different root cause without contradicting anything stated here.
Distinct publishers with included, body-backed reporting in this cluster.
dev.to
1 article · August 17, 2026