Repository navigation
Queue producer deploy relies on name filtering absent from the published Queues API contract #313
Description
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-probeHowever, 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/config0.25.0 (through cf)@cloudflare/vite-plugin2.0.0-beta.sha-8c6014f9a; Vite 8.3.4- Undici 7.29.1 MockAgent for synthetic HTTP only
Published contract and implementation references
- Queues List API: path parameter
account_id; no query parameters documented. - OpenAPI at e9f3700:
queues-listdeclares onlyaccount_id. - TypeScript SDK at 6dbe1fc:
QueueListParamscontains onlyaccount_id; list usesSinglePage. - Official deploy helper at 5f12934: adds repeated
nameparameters; validates membership only when response/request lengths differ. - cf's own name-lookup mock at 4a1df32: explicitly expects the
namequery.
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.1Create 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=falseObserved with the same configuration/policies in a subprocess test:
- Build and individual Queue GET succeed.
- Query-rejection mode: deploy sends
?name=synthetic-queue-probe, fails on the synthetic 400, and makes no write request. - 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