paritytech / paritytech/contract-dependency-manager
cdm deploy: Bulletin publish crashes on 'blocks.get(acc.best).number' (polkadot-api observable-client bug)
Nobody has claimed this yet.
- Dominant language
- TypeScript
- Stars
- 4
- Forks
- 3
- Avg merge
- 4d 13h
- Merged PRs (30d)
- 3
Description
Summary
cdm deploy crashes during the Bulletin metadata-publish step against the Paseo Bulletin node, with:
[Bulletin publish item 1/1] Metadata publish failed (metadata-0): [Bulletin publish] undefined is not an object (evaluating 'blocks.get(acc.best).number')
The first contract fails and all dependents are skipped (dependency failed).
Repro
cdm deploy -n paseo
AssetHub ✔ wss://paseo-asset-hub-next-rpc.polkadot.io
Bulletin ✔ wss://paseo-bulletin-next-rpc.polkadot.io
Registry 0x7671a84f5e7b1bf704f0ad3f43a185ff3d4b303f
@d3pot/organization ✖ ← Metadata publish failed
@d3pot/repository ✖ ← Skipped: dependency failed
@d3pot/pull-request ✖ ← Skipped: dependency failed
AssetHub connects and the registry read succeeds; only the Bulletin publish path crashes.
Root cause
The error is not in CDM code — it originates in polkadot-api's @polkadot-api/observable-client chainHead block-tracking reducer, in the finalized event handler:
@polkadot-api/observable-client@0.18.7/dist/chainHead/streams/pinned-blocks.js:194
case "finalized": {
const finalized = event.finalizedBlockHashes.slice(-1)[0];
...
acc.finalized = finalized;
const { blocks } = acc;
if (blocks.get(acc.best).number < blocks.get(acc.finalized).number) // ← acc.best not in map → undefined.number → throws
acc.best = acc.finalized;
blocks.get(acc.best) returns undefined because on this finalized notification acc.best still points to a block hash that is no longer in the pinned blocks map. The line dereferences .number without a guard — unlike the sibling finalized-height reads in the same file, which use ?.number ?? -1.
Call path: MetadataPublisher.publish() (src/lib/contracts/src/publisher.ts:42) → submitAndWatch() (@parity/product-sdk-tx) → papi chainHead subscription → the reducer above → error propagates and is wrapped as [Bulletin publish] ... at src/lib/contracts/src/publisher.ts:45.
Why AssetHub works but Bulletin doesn't: both connect over raw WebSocket via createDirectChainClient (src/lib/env/src/connection.ts:193, createClient(getWsProvider(...))). The Paseo Bulletin node's chainHead finalized/pruning event sequence leaves acc.best dangling; AssetHub's does not.
Why upgrading doesn't fix it
The latest @polkadot-api/observable-client is 0.18.7 (2026-06-02) and line 194 is still unguarded — no upstream fix exists yet.
(Separately: node_modules is stale — the catalog pins polkadot-api: ^2.1.7 in pnpm-workspace.yaml but 2.1.2/2.1.5 are installed. Worth a pnpm install, but it does not fix this bug.)
Options
pnpm patch@polkadot-api/observable-client— guard line 194 like its siblings:(blocks.get(acc.best)?.number ?? -1) < .... Surgical, no resubmission risk, but a vendored patch to maintain until upstream ships a fix. (recommended immediate fix)- Report upstream to polkadot-api with the Paseo Bulletin repro so the
finalizedhandler gets the same?.guard the finalized-height reads already use — then the local patch can be dropped. - Retry the publish in
publisher.tsviawithRetryfrom@parity/product-sdk-tx(already available). Recovers the transient race, but re-runssubmitAndWatch→ risks double-storing metadata since the crash is in block-tracking, not submission. Would need a CID existence check before resubmit.
Suggested plan
Apply option 1 now, file option 2 upstream, drop the patch once upstream fixes it.
Contributor guide
No contributing guide indexed for this repository
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start with src/lib/contracts/src/publisher.ts:42 and :45, then trace submitAndWatch through @polkadot-api/observable-client and the pinned-blocks.js finalized handler at line 194. Reproduce with cdm deploy -n paseo and inspect the pnpm patch workflow. Done means the Bulletin metadata publish completes without the reducer crash, with an upstream report filed and the local patch removable after a fix.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- typescript
- Domain
- backend-api-design, blockchain
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Quiet
- Clarity
- Clearly specified
- Newbie friendliness
- 48/100