You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Active-contract queries run at offset 0 and return an empty set on a live node #40
Active-contract queries are issued at ledger offset zero, the beginning of the ledger. On a live participant the active contract set at the beginning of the ledger is empty, so every query returns nothing and the service behaves as though no instrument, holding, or instruction exists.
Steps to reproduce
Start a Canton participant with the JSON Ledger API enabled and upload the DAR.
Seed an operator, a registry, and one instrument.
Ask the service to list instruments.
Expected vs actual behavior
Expected: the seeded instrument is returned.
Actual: an empty list, because the query snapshots the ledger at its beginning rather than at the current offset.
Additional context
Part of #23. Blocks the end-to-end transfer test and the CLI conformance check, and should be fixed alongside the filter defect since both surface on the first live-node request.
Not caught by the current tests: the in-memory ledger stub ignores the offset entirely.
Confirmed against a live node, and the fix is one line
Reproduced exactly as described, then proved the diagnosis by fixing it. Environment: Canton 3.5.6 via dpm sandbox, seeded with scripts/seed.mjs (an operator, an instrument admin, a TokenRegistry, and one CCInstrumentConfig created through propose/accept).
Reproduction
With the service pointed at the seeded sandbox, the instrument exists on the ledger but the read path reports nothing:
GET /registry/metadata/v1/instruments -> {"instruments":[]}
/healthz, /readyz, and /registry/metadata/v1/info all answer correctly, so the service is up, the config is valid, and the operator party resolves. /readyz is worth calling out: it reports ready in this state, because the underlying query succeeds and simply returns an empty set. Readiness cannot distinguish "ledger reachable" from "ledger reachable but every query is blind".
Proof of the diagnosis
Patching only activeAtOffset in the built output, so that it reads the current offset first, and changing nothing else:
Both metadata read paths immediately return the seeded instrument:
GET /registry/metadata/v1/instruments
{"instruments":[{"id":"CC","name":"Canton Coin Forge","symbol":"CC","decimals":10,"supportedApis":{...}}]}
GET /registry/metadata/v1/instruments/CC
{"id":"CC","name":"Canton Coin Forge","symbol":"CC","decimals":10,"supportedApis":{...}}
The patch was reverted; it was a throwaway experiment to isolate the cause, not the fix. The registry suite is still 67/67 green.
So the offset really is the whole defect on the read path, and no other change is needed to make metadata reads work live.
Technical notes for the fix
The offset comes from GET /v2/state/ledger-end, which returns {"offset": <n>}. On a freshly started sandbox with a DAR uploaded it was already 8, which is why offset 0 returns nothing.
Every activeContracts call needs a current offset, so the natural shape is to resolve the ledger end inside HttpLedgerClient.activeContracts rather than at each of the call sites in lookup.ts and the routes.
That costs one extra round trip per query. Several call sites already run two queries concurrently via Promise.all, so if the extra hop matters the offset can be fetched once per request and threaded through, but correctness first.
Activity
lmcorbalan commentedon Aug 4, 2026
Confirmed against a live node, and the fix is one line
Reproduced exactly as described, then proved the diagnosis by fixing it. Environment: Canton 3.5.6 via
dpm sandbox, seeded withscripts/seed.mjs(an operator, an instrument admin, aTokenRegistry, and oneCCInstrumentConfigcreated through propose/accept).Reproduction
With the service pointed at the seeded sandbox, the instrument exists on the ledger but the read path reports nothing:
/healthz,/readyz, and/registry/metadata/v1/infoall answer correctly, so the service is up, the config is valid, and the operator party resolves./readyzis worth calling out: it reports ready in this state, because the underlying query succeeds and simply returns an empty set. Readiness cannot distinguish "ledger reachable" from "ledger reachable but every query is blind".Proof of the diagnosis
Patching only
activeAtOffsetin the built output, so that it reads the current offset first, and changing nothing else:Both metadata read paths immediately return the seeded instrument:
The patch was reverted; it was a throwaway experiment to isolate the cause, not the fix. The registry suite is still 67/67 green.
So the offset really is the whole defect on the read path, and no other change is needed to make metadata reads work live.
Technical notes for the fix
GET /v2/state/ledger-end, which returns{"offset": <n>}. On a freshly started sandbox with a DAR uploaded it was already8, which is why offset 0 returns nothing.activeContractscall needs a current offset, so the natural shape is to resolve the ledger end insideHttpLedgerClient.activeContractsrather than at each of the call sites inlookup.tsand the routes.Promise.all, so if the extra hop matters the offset can be fetched once per request and threaded through, but correctness first.registry/src/ledger.ts).Full detail of the conventions this participant enforces is recorded in tracked
RUNBOOK.md.