Skip to content

Vault listObjects does not respect the order or before parameters and has broken pagination #1516

@rodobre

Description

@rodobre

Summary

vault.listObjects has two issues:

  1. The SDK implementation does not forward the order and before parameters to the API
  2. The API does not return usable pagination cursors in listMetadata

This causes silent data loss when a vault contains more objects than the default page size.

1. SDK: order and before parameters are silently ignored

The PaginationOptions interface accepts order and before, but vault.listObjects never passes it to the API:

https://github.com/workos/workos-node/blob/main/src/vault/vault.ts#L84

async listObjects(options?: PaginationOptions): Promise<List<ObjectDigest>> {
  const url = new URL('/vault/v1/kv', this.workos.baseURL);
  if (options?.after) {
    url.searchParams.set('after', options.after);
  }
  if (options?.limit) {
    url.searchParams.set('limit', options.limit.toString());
  }
  // `order` | `before` is never added to the URL
}

2. Pagination is not correct

When running the following snippet of code, the listMetadata object that is returned seems to be wrong, it returns a before pointer but not an after:

await getWorkOS().vault.listObjects({
    limit: 2,
    order: 'asc',
  });

Returns:

{
  "object": "list",
  "data": [
    {
      "id": "secret_ABC",
      "name": "SECRET_1",
      "updatedAt": "2026-01-01T00:00:00.000Z"
    },
    {
      "id": "secret_XYZ",
      "name": "SECRET_2",
      "updatedAt": "2026-01-01T00:00:00.000Z"
    }
  ],
  "listMetadata": {
    "before": "secret_ABC"
  }
}

This is different from running the same bit of code but for listing organizations:

await getWorkOS().organizations.listOrganizations({
    limit: 2,
    order: 'asc'
})
{
  "object": "list",
  "options": {
    "limit": 2
  },
  "list": {
    "object": "list",
    "data": [
      {
        "object": "organization",
        "id": "org_ABC",
        "domains": [
          {
            "id": "org_ABC",
            "organizationId": "org_ABC",
            "createdAt": "2026-01-01T00:00:00.000Z",
            "updatedAt": "2026-01-01T00:00:00.000Z"
          }
        ],
        "createdAt": "2026-01-01T00:00:00.000Z",
        "updatedAt": "2026-01-01T00:00:00.000Z",
        "metadata": {}
      },
      {
        "object": "organization",
        "id": "org_XYZ",
        "domains": [],
        "createdAt": "2026-01-01T00:00:00.000Z",
        "updatedAt": "2026-01-01T00:00:00.000Z",
        "metadata": {}
      }
    ],
    "listMetadata": {
      "before": null,
      "after": "org_XYZ"
    }
  }
}

What happens, however, is that for Vault objects you can still treat the last object as a tentative after and the API will return results. But after still seems to look, weird?

await getWorkOS().vault.listObjects({
    limit: 2,
    order: 'asc',
    after: 'secret_XYZ',
});
{
  "object": "list",
  "data": [
    {
      "id": "secret_123",
      "name": "SECRET_3",
      "updatedAt": "2026-01-01T00:00:00.000Z"
    },
    {
      "id": "secret_456",
      "name": "SECRET_4",
      "updatedAt": "2026-01-01T00:00:00.000Z"
    }
  ],
  "listMetadata": {
    "before": "secret_123",
    "after": "secret_123"
  }
}

PR

This PR addresses the argument forwarding but not the pagination inconsistencies as those are internal to WorkOS.

#1517

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions