Skip to content
Discussions/App Development/Getting Empty Contracts fromForum ↗

Getting Empty Contracts from

App Development11 posts76 views1 likesLast activity Oct 2025
OH
ohthepainOP
Oct 2025

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!

WA
WallaceKelly
Oct 2025

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?

OH
ohthepain
Oct 2025

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": {}
  }
]
   
WA
WallaceKelly
Oct 2025

Do you get a different result if you capitalize TemplateFilter?

OH
ohthepain
Oct 2025

The compiler complains if I try:

WA
WallaceKelly
Oct 2025

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?

OH
ohthepain
Oct 2025

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.

WA
WallaceKelly
Oct 2025

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
           :
OH
ohthepain
Oct 2025

It looks like that was it. Thank you!

Scary stuff!

WA
WallaceKelly
Oct 2025

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

OH
ohthepain
Oct 2025

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"]),
  };
}

← Back to Discussions