Getting Empty Contracts from

I’m trying to query active contracts using the HTTP API, but all I am getting back is workflowId’s and empty contractEntry objects. I am on 3.3 and trying to get the active contracts at ledger-end. I am following the docs here: JSON Ledger API Service V2 — Digital Asset’s platform documentation

How do I get the info out of the contractEntry objects, or some reference to the contract?

I am trying to get the contracts like this:

  static async listActiveContracts(filters: ContractFilters = {}): Promise<any[]> {
    try {
      // We are using a single participant atm
      const token = await TokenGenerator.generateUserValidatorToken();
      const config = new Configuration({
        basePath: process.env.USER_PARTICIPANT_URL!,
        headers: {
          Authorization: `Bearer ${token}`,
        },
      });
      const api = new DefaultApi(config);

      const ledgerEnd = await api.getV2StateLedgerEnd();
      const request: GetActiveContractsRequest = {
        verbose: true,
        activeAtOffset: ledgerEnd.offset,
        filter: {
          filtersByParty: filters.partyId
            ? {
                [filters.partyId]: {
                  cumulative: [
                    {
                      identifierFilter: {
                        templateFilter: {
                          value: {
                            templateId: filters.contractType,
                            includeCreatedEventBlob: true,
                          },
                        },
                      },
                    },
                  ],
                },
              }
            : {},
        },
      };

      const response = await api.postV2StateActiveContracts({ getActiveContractsRequest: request });
      console.log("Raw response structure:", JSON.stringify(response, null, 2));

      const contracts = response.map((r) => r.contractEntry);
      return contracts;
    } catch (error: unknown) {
      console.error("Error listing active contracts:", error);
      if (error instanceof Error) {
        throw new Error(`Failed to list active contracts: ${error.message}`);
      }
      throw new Error("Failed to list active contracts: Unknown error");
    }
  }

The response seems to contain transaction events and empty contractEntry objects:

[
  {
    "workflowId": "exercise-InvestmentPool_Expire-1760613502733",
    "contractEntry": {}
  },
  {
    "workflowId": "exercise-InvestmentPool_Expire-1760613549749",
    "contractEntry": {}
  },
  {
    "workflowId": "exercise-InvestmentPool_Archive-1760614505206",
    "contractEntry": {}
  },
  {
    "workflowId": "exercise-InvestmentPool_Archive-1760614608198",
    "contractEntry": {}
  },
  {
    "workflowId": "exercise-InvestmentPool_Unarchive-1760615662226",
    "contractEntry": {}
  },
  {
    "workflowId": "exercise-InvestmentPool_Unarchive-1760615889958",
    "contractEntry": {}
  },
  {
    "workflowId": "exercise-InvestmentPool_Archive-1760617507418",
    "contractEntry": {}
  },
  {
    "workflowId": "exercise-InvestmentPool_Archive-1760618108291",
    "contractEntry": {}
  },
  {
    "workflowId": "exercise-InvestmentPool_GoLive-1760945022994",
    "contractEntry": {}
  },
  {
    "workflowId": "create-contract",
    "contractEntry": {}
  },
  {
    "workflowId": "create-contract",
    "contractEntry": {}
  },
  {
    "workflowId": "create-contract",
    "contractEntry": {}
  }
]

Decoded token payload: {
  exp: 1761113053,
  iat: 1761112753,
  jti: 'trrtcc:0ba4a47c-23f4-510b-48a2-11246c4082a9',
  iss: 'https://keycloak.dserv.io/realms/AppUser',
  aud: [ 'https://canton.network.global', 'account' ],
  sub: 'c5d77…XXXXX…e1fbb33',
  typ: 'Bearer',
  azp: 'app-user-validator',
  acr: '1',
  'allowed-origins': [ '/*' ],
  realm_access: {
    roles: [ 'offline_access', 'uma_authorization', 'default-roles-appuser' ]
  },
  resource_access: { account: { roles: [Array] } },
  scope: 'profile email',
  email_verified: false,
  clientHost: '85.230.60.110',
  preferred_username: 'service-account-app-user-validator',
  clientAddress: '85.230.60.110',
  client_id: 'app-user-validator'
}

Here is the result from creating a contract:

{
  "transaction": {
    "updateId": "1220fe616dd17534f96894e92a8c46176a289c10711ea544a9e19057d60a908c32d9",
    "commandId": "create-contract-1761110933730",
    "workflowId": "create-contract",
    "effectiveAt": "2025-10-22T05:28:54.872955Z",
    "events": [
      {
        "CreatedEvent": {
          "offset": 228985,
          "nodeId": 0,
          "contractId": "00a25eaa50483c6ac26f707a0e2b69f5771b74e99318abd472ed1a2d9b7ab84694ca1112205739f97109dcc8c8ca8645fd65e585a6f79d8a1fa766b79830b7b14a8c533885",
          "templateId": "6bb48e1c7f34a2263e0024dc4e9d84ef33bee85ee0974ef46379c7759e9a2125:Dserv.Investment:InvestmentPool",
          "contractKey": null,
          "createArgument": {
            "name": "e42190f9-e6fe-4741-b83f-f0aa62ca233b",
            "status": "TOKENIZED",
            "provider": "user_tenant12_at_abc123_dot_com::12204045cfaa811009ac22de6f94424284a8fdd20fc54a84cf0a87cb46ac7581dac8",
            "numTokensInit": "210000.00",
            "numTokensAvailable": "210000.00",
            "id": "Loan-2025-10-22T05-28-49-0"
          },
          "createdEventBlob": "",
          "interfaceViews": [],
          "witnessParties": [
            "user_tenant12_at_abc123_dot_com::12204045cfaa811009ac22de6f94424284a8fdd20fc54a84cf0a87cb46ac7581dac8"
          ],
          "signatories": [
            "user_tenant12_at_abc123_dot_com::12204045cfaa811009ac22de6f94424284a8fdd20fc54a84cf0a87cb46ac7581dac8"
          ],
          "observers": [],
          "createdAt": "2025-10-22T05:28:54.872955Z",
          "packageName": "daml-dserv"
        }
      }
    ],
    "offset": 228985,
    "synchronizerId": "global-domain::1220be58c29e65de40bf273be1dc2b266d43a9a002ea5b18955aeef7aac881bb471a",
    "traceContext": {
      "traceparent": "00-b022dc515821f263f84c4de1dca56145-b9090e06de0c57fc-01",
      "tracestate": null
    },
    "recordTime": "2025-10-22T05:28:55.152011Z"
  }
}

Thanks all!

This one has me stumped too.

Any chance that request does not have the value we think? Want to JSON.stringify it so that we can double-check it?

Sure, and thank you! I added the line


      console.log("Requesting active contracts with:", JSON.stringify(request, null, 2));

      const response = await api.postV2StateActiveContracts({ getActiveContractsRequest: request });
      console.log("Raw response structure:", JSON.stringify(response, null, 2));

and here is the output:

Decoded token payload: {
  exp: 1761149346,
  iat: 1761149046,
  jti: 'trrtcc:a514ce85-d913-a744-7e6e-69faa628037b',
  iss: 'https://keycloak.dserv.io/realms/AppUser',
  aud: [ 'https://canton.network.global', 'account' ],
  sub: 'c5d7778a-ce31-41e3-9c82-34f03e1fbb33',
  typ: 'Bearer',
  azp: 'app-user-validator',
  acr: '1',
  'allowed-origins': [ '/*' ],
  realm_access: {
    roles: [ 'offline_access', 'uma_authorization', 'default-roles-appuser' ]
  },
  resource_access: { account: { roles: [Array] } },
  scope: 'profile email',
  email_verified: false,
  clientHost: '85.230.60.110',
  preferred_username: 'service-account-app-user-validator',
  clientAddress: '85.230.60.110',
  client_id: 'app-user-validator'
}
Token verified successfully
Requesting active contracts with: {
  "verbose": true,
  "activeAtOffset": 232434,
  "filter": {
    "filtersByParty": {
      "user_tenant12_at_abc123_dot_com::12204045cfaa811009ac22de6f94424284a8fdd20fc54a84cf0a87cb46ac7581dac8": {
        "cumulative": [
          {
            "identifierFilter": {
              "templateFilter": {
                "value": {
                  "templateId": "#daml-dserv:Dserv.Investment:InvestmentPool",
                  "includeCreatedEventBlob": true
                }
              }
            }
          }
        ]
      }
    }
  }
}
body {
  filter: {
    filtersByParty: {
      'user_tenant12_at_abc123_dot_com::12204045cfaa811009ac22de6f94424284a8fdd20fc54a84cf0a87cb46ac7581dac8': [Object]
    },
    filtersForAnyParty: undefined
  },
  verbose: true,
  activeAtOffset: 232434,
  eventFormat: undefined
}
Raw response structure: [
  {
    "workflowId": "exercise-InvestmentPool_Expire-1760613502733",
    "contractEntry": {}
  },
  {
    "workflowId": "exercise-InvestmentPool_Expire-1760613549749",
    "contractEntry": {}
  },
  {
    "workflowId": "exercise-InvestmentPool_Archive-1760614505206",
    "contractEntry": {}
  },
  {
    "workflowId": "exercise-InvestmentPool_Archive-1760614608198",
    "contractEntry": {}
  },
  {
    "workflowId": "exercise-InvestmentPool_Unarchive-1760615662226",
    "contractEntry": {}
  },
  {
    "workflowId": "exercise-InvestmentPool_Unarchive-1760615889958",
    "contractEntry": {}
  },
  {
    "workflowId": "exercise-InvestmentPool_Archive-1760617507418",
    "contractEntry": {}
  },
  {
    "workflowId": "exercise-InvestmentPool_Archive-1760618108291",
    "contractEntry": {}
  },
  {
    "workflowId": "exercise-InvestmentPool_GoLive-1760945022994",
    "contractEntry": {}
  },
  {
    "workflowId": "create-contract",
    "contractEntry": {}
  },
  {
    "workflowId": "create-contract",
    "contractEntry": {}
  },
  {
    "workflowId": "create-contract",
    "contractEntry": {}
  }
]
   

Do you get a different result if you capitalize TemplateFilter?

The compiler complains if I try:

This may come down to differences in versions.

If I do this…

curl http://localhost:7575/docs/openapi > openapi.yaml

…then I can see that TemplateFilter is expected to be PascalCase. Submitting with TemplateFilter succeeds. Submitting with templateFilter fails:

Invalid value for: body (JSON decoding to CNil should never happen at 'identifierFilter')

I’m using the Splice release 0.4.20. You?

I was on 0.4.18. I updated to 0.4.20 and regenerated the clients but still get the same result.

I can also see PascalCase in the OpenAPI spec but the generated OpenAPI client expects camelCase.

Thank you for upgrading and regenerating the OpenAPI client.

I wonder if the generated OpenAPI client is not deserializing the raw response correctly. It is likely you are receiving back a complete response in the raw. But the contractEntry fields are printed as empty objects. The values on those contractEntry fields are expected to be type JsActiveContract.

[
  {
    "workflowId": "exercise-InvestmentPool_Expire-1760613502733",
    "contractEntry": {}
  },
    :
    :

Is it possible that the auto-generated OpenAPI client is expecting jsActiveContract (lower-case) and ignoring the JsActiveContract (upper-case) in the raw response?

The Open API spec:

    JsContractEntry:
      title: JsContractEntry
            :
      oneOf:
      - type: object
        required:
        - JsActiveContract
           :

It looks like that was it. Thank you!

Scary stuff!

1 Like

For those who follow your footsteps, how did you “fix” it? Is there an option available in your OpenAPI-generated client?

I wasn’t able to fix the code generation. I had to modify JsGetActiveContractsResponse to handle both camelCase and PascalCase.

So in JsContractEntryOneOfFromJSONTyped I changed

  • return {
  •    'jsActiveContract': JsActiveContractFromJSON(json['JsActiveContract']),
    
  • };

to

// Handle both PascalCase and camelCase

const activeContract = json[“JsActiveContract”] || json[“jsActiveContract”];

return {

jsActiveContract: JsActiveContractFromJSON(activeContract),

};}

and in instanceOfJsContractEntryOneOf I changed

if (!('jsActiveContract' in value) || value['jsActiveContract'] === undefined) return false;

to

  // Check for both PascalCase and camelCase versions
  if (
    ("JsActiveContract" in value && value["JsActiveContract"] !== undefined) ||
    ("jsActiveContract" in value && value["jsActiveContract"] !== undefined)
  ) {
    return true;
  }
  return false;

Here is the entire file:

/* tslint:disable */
/* eslint-disable */
/**
 * JSON Ledger API HTTP endpoints
 * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator)
 *
 * The version of the OpenAPI document: 3.3.0-SNAPSHOT
 *
 *
 * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
 * https://openapi-generator.tech
 * Do not edit the class manually.
 */

import { mapValues } from "../runtime";
import type { JsActiveContract } from "./JsActiveContract";
import {
  JsActiveContractFromJSON,
  JsActiveContractFromJSONTyped,
  JsActiveContractToJSON,
  JsActiveContractToJSONTyped,
} from "./JsActiveContract";

/**
 *
 * @export
 * @interface JsContractEntryOneOf
 */
export interface JsContractEntryOneOf {
  /**
   *
   * @type {JsActiveContract}
   * @memberof JsContractEntryOneOf
   */
  jsActiveContract: JsActiveContract;
}

/**
 * Check if a given object implements the JsContractEntryOneOf interface.
 */
export function instanceOfJsContractEntryOneOf(value: object): value is JsContractEntryOneOf {
  // Check for both PascalCase and camelCase versions
  if (
    ("JsActiveContract" in value && value["JsActiveContract"] !== undefined) ||
    ("jsActiveContract" in value && value["jsActiveContract"] !== undefined)
  ) {
    return true;
  }
  return false;
}

export function JsContractEntryOneOfFromJSON(json: any): JsContractEntryOneOf {
  return JsContractEntryOneOfFromJSONTyped(json, false);
}

export function JsContractEntryOneOfFromJSONTyped(json: any, ignoreDiscriminator: boolean): JsContractEntryOneOf {
  if (json == null) {
    return json;
  }
  // Handle both PascalCase and camelCase
  const activeContract = json["JsActiveContract"] || json["jsActiveContract"];
  return {
    jsActiveContract: JsActiveContractFromJSON(activeContract),
  };
}

export function JsContractEntryOneOfToJSON(json: any): JsContractEntryOneOf {
  return JsContractEntryOneOfToJSONTyped(json, false);
}

export function JsContractEntryOneOfToJSONTyped(
  value?: JsContractEntryOneOf | null,
  ignoreDiscriminator: boolean = false
): any {
  if (value == null) {
    return value;
  }

  return {
    JsActiveContract: JsActiveContractToJSON(value["jsActiveContract"]),
  };
}