cloudflare / cloudflare/cloudflare-typescript

AI Gateway logs.list() filters parameter is silently ignored due to qs serialization with arrayFormat: 'repeat'

Open
#2,723 1 comment 0 reactions 0 assignees View on GitHub
Dominant language
TypeScript
Stars
814
Forks
190
Avg merge
1h 38m
Merged PRs (30d)
2

Description

## Description

The `client.aiGateway.logs.list()` method accepts a typed `filters` parameter (`Array`), but the filters are silently ignored by the API because `stringifyQuery` serializes them in a format the API doesn't understand.

## Root cause

The Cloudflare SDK overrides `stringifyQuery` in `src/index.ts:480`:

```ts
protected override stringifyQuery(query: Record): string {
return qs.stringify(query, { allowDots: true, arrayFormat: 'repeat' });
}
```

With `arrayFormat: 'repeat'` and `allowDots: true`, an array of filter objects like:

```ts
filters: [
{ key: "metadata.key", operator: "eq", value: ["requestId"] },
{ key: "metadata.value", operator: "eq", value: ["abc-123"] },
]
```

Gets serialized as:

```
filters.key=metadata.key&filters.operator=eq&filters.value=requestId&filters.key=metadata.value&filters.operator=eq&filters.value=abc-123
```

This flat serialization **loses the array structure** — the API can't determine which `key` belongs to which `operator`/`value`. The result is the API ignoring the filters entirely and returning all logs.

The correct format (which works when using `fetch` directly) is JSON-encoded:

```
filters=%5B%7B%22key%22%3A%22metadata.key%22%2C%22operator%22%3A%22eq%22%2C%22value%22%3A%5B%22requestId%22%5D%7D%2C...%5D
```

i.e. `filters=[{"key":"metadata.key","operator":"eq","value":["requestId"]},...]`

## Reproduction

```ts
import Cloudflare from "cloudflare";

const client = new Cloudflare({ apiToken: "..." });

const logs = [];
for await (const log of client.aiGateway.logs.list("my-gateway", {
account_id: "...",
filters: [
{ key: "metadata.key", operator: "eq", value: ["requestId"] },
{ key: "metadata.value", operator: "eq", value: ["some-uuid"] },
],
})) {
logs.push(log);
}

console.log(logs.length);
// Expected: 1 (only matching logs)
// Actual: ALL logs in the gateway (filters ignored)
```

## Workaround

Use `fetch` directly with JSON-encoded filters:

```ts
const filters = JSON.stringify([
{ key: "metadata.key", operator: "eq", value: ["requestId"] },
{ key: "metadata.value", operator: "eq", value: [requestId] },
]);
const url = `https://api.cloudflare.com/client/v4/accounts/${accountId}/ai-gateway/gateways/${gatewayId}/logs?filters=${encodeURIComponent(filters)}`;

const res = await fetch(url, {
headers: { Authorization: `Bearer ${apiToken}` },
});
```

## Affected methods

This likely affects any method using the `filters` query parameter with arrays of objects:

- `client.aiGateway.logs.list()`
- `client.aiGateway.logs.delete()`
- `client.aiGateway.datasets.list()` (if it uses the same filter format)

## Environment

- SDK version: `cloudflare@5.2.0`
- Runtime: Cloudflare Workers (also reproducible in Node.js)

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.