Skip to content

Queue producer deploy relies on name filtering absent from the published Queues API contract #313

Closed

Description

@katsu996

Queue producer deploy depends on a name filter absent from the published Queues API contract

Summary of the issue

cf deploy --prebuilt --provision=false with a named Queue producer sends:

GET /accounts/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/queues?name=synthetic-queue-probe

However, cf schema queues list, the published Queues List API reference/OpenAPI, and the generated TypeScript SDK currently expose only account_id, with no name query parameter.

The name filter is clearly intentional in the official implementation/tests. This report asks for clarification/alignment of the supported API contract, not a claim that the live API rejects the filter or binds the wrong queue. All reproduction traffic below is mocked; no real account is used.

This matters when verifying deployment on an account with multiple pages of queues: without a documented exact-name filtering contract, a successful name lookup in a mock cannot establish that an existing queue outside the unfiltered first page is resolved correctly.

Expected behaviour

Please clarify/document the supported name-filter semantics (exact match vs substring, repeated name values, pagination and result_info semantics under filtering), and align OpenAPI/generated SDK/cf schema accordingly. Alternatively, a supported Queue ID binding path would avoid the ambiguity.

There is also a defensive validation observation: if a filtered lookup returns one unrelated queue for one requested name, cf proceeds to Worker upload because the preflight compares lengths before validating membership. Consider checking the expected names even when lengths are equal. This is a mocked robustness case, not an observed production API response.

Version of cf used

1.0.0-beta.16 (installed version, reproduced). Latest release observed is beta.17; this report does not claim that beta.17 was runtime-tested.

System information and package versions

  • macOS arm64; Node 24.18.0; pnpm 11.28.5
  • @cloudflare/config 0.25.0 (through cf)
  • @cloudflare/vite-plugin 2.0.0-beta.sha-8c6014f9a; Vite 8.3.4
  • Undici 7.29.1 MockAgent for synthetic HTTP only

Published contract and implementation references

Related: #20 / #120 concern list truncation, but this report is specifically about the producer deploy path and its name-filter contract, not a request to enumerate the entire account for every deployment.

Reproduction (synthetic, no Cloudflare credentials needed)

In a disposable directory, install the pinned toolchain (not instructions to change any production project's dependencies):

pnpm init
pnpm add -D cf@1.0.0-beta.16 @cloudflare/vite-plugin@2.0.0-beta.sha-8c6014f9a vite@8.3.4 undici@7.29.1

Create cloudflare.config.ts:

import { bindings, defineConfig } from 'cf/config';
export default defineConfig({
  accountId: 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa',
  worker: {
    name: 'synthetic-worker',
    entrypoint: './worker.ts',
    compatibilityDate: '2026-10-13',
    workersDev: false,
    previewUrls: false,
    env: { QUEUE: bindings.queue({ name: 'synthetic-queue-probe' }) },
  },
});

Create worker.ts:

export default { fetch() { return new Response('ok'); } };

Create vite.config.mts:

import { cloudflare } from '@cloudflare/vite-plugin';
import { defineConfig } from 'vite';
export default defineConfig({ plugins: [cloudflare()] });

Create mock.mjs:

import { MockAgent, setGlobalDispatcher } from 'undici';
const agent = new MockAgent();
agent.disableNetConnect();
// Allow only the local build runtime, never a real remote API.
agent.enableNetConnect(/^(127\.0\.0\.1|localhost|\[::1\]):/);
setGlobalDispatcher(agent);
const envelope = (result, statusCode = 200, extra = {}) => ({
  statusCode,
  data: { success: statusCode === 200, result, ...extra },
  responseOptions: { headers: { 'content-type': 'application/json' } },
});
agent.get('https://api.cloudflare.com')
  .intercept({ path: () => true, method: () => true })
  .reply(({ path, method }) => {
    const url = new URL(path, 'https://api.cloudflare.com');
    console.error(method, url.pathname, url.search); // Synthetic paths only, no headers.
    if (method === 'GET' && url.pathname.endsWith('/queues')) {
      if (process.env.MOCK_MODE !== 'first-page-only') {
        return envelope(null, 400, {
          errors: [{ code: 1000, message: 'SYNTHETIC_UNSUPPORTED_QUEUE_QUERY' }],
        });
      }
      return envelope([
        { queue_id: 'dddddddddddddddddddddddddddddddd', queue_name: 'unrelated-queue' },
      ], 200, {
        result_info: { count: 1, page: 1, per_page: 1, total_count: 2, total_pages: 2 },
      });
    }
    if (method === 'GET' && url.pathname.endsWith('/queues/cccccccccccccccccccccccccccccccc')) {
      return envelope({ queue_id: 'cccccccccccccccccccccccccccccccc', queue_name: 'synthetic-queue-probe' });
    }
    if (method === 'GET' && /\/workers\/(services|scripts)\//.test(url.pathname)) {
      return envelope(null, 404, { errors: [{ code: 10007, message: 'Worker not found' }] });
    }
    if (method === 'PUT' && url.pathname.endsWith('/workers/scripts/synthetic-worker')) {
      // Deliberately terminate before simulating a successful upload.
      return envelope(null, 400, { errors: [{ code: 1000, message: 'SYNTHETIC_UPLOAD_REACHED' }] });
    }
    throw new Error('UNEXPECTED_MOCK_REQUEST');
  }).persist();

Then run (all identifiers/tokens are synthetic):

export CLOUDFLARE_API_TOKEN=synthetic-token
export CLOUDFLARE_ACCOUNT_ID=aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
export CF_TELEMETRY_DISABLED=1 WRANGLER_SEND_METRICS=false CI=true
export NODE_OPTIONS="--import=$(node -e 'console.log(require("node:url").pathToFileURL(process.cwd()+"/mock.mjs").href)')"
pnpm exec cf build --mode development
pnpm exec cf queues get cccccccccccccccccccccccccccccccc
pnpm exec cf deploy --prebuilt --mode development --provision=false
MOCK_MODE=first-page-only pnpm exec cf deploy --prebuilt --mode development --provision=false

Observed with the same configuration/policies in a subprocess test:

  1. Build and individual Queue GET succeed.
  2. Query-rejection mode: deploy sends ?name=synthetic-queue-probe, fails on the synthetic 400, and makes no write request.
  3. First-page-only mode: deploy sees an unrelated queue, does not fetch the expected ID or another page, and reaches Worker PUT. The mock deliberately rejects it with SYNTHETIC_UPLOAD_REACHED.

The mock's 400 responses are not evidence of live API behavior. The question is which public filtering contract deployment clients should rely on, and how to validate that contract for queues outside the unfiltered first page.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

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