Skip to content

Cloudflare deploy fails with "binding X cannot be uploaded with pinned secrets" when Worker has a ratelimits binding and any secret #462

Open

Description

@madebysandro

Versions

  • void 1.1.11, @void/deploy-cloudflare 1.1.11 (latest on npm as of 2026-10-16)
  • void deploy --platform cloudflare (direct Cloudflare target), Node 24, Workers Builds and local

Repro

  1. Project with a rate limit binding in void.config.ts:
    cloudflare: {
      ratelimits: [
        { name: "MY_RATE_LIMIT", namespace_id: "1001", simple: { limit: 30, period: 60 } },
      ],
    }
  2. void deploy --platform cloudflare → succeeds (Worker has no secrets yet).
  3. Set any Worker secret (void secret put FOO, or wrangler/cf workers secrets put).
  4. void deploy --platform cloudflare again → fails:
    ■ deploy: Cloudflare Worker Version upload failed (exit 1); production traffic is unchanged.
      Cloudflare binding MY_RATE_LIMIT cannot be uploaded with pinned secrets.
    
    Every subsequent deploy fails the same way while any secret exists.

Root cause

  • @void/deploy-cloudflare/dist/index.mjs (activateCloudflareVersionWithSecretsFile, ~L1354): when the active version has any secret and no candidate secrets file is passed, Void calls pinActiveSecrets and routes the upload through uploadPinnedCloudflareVersion.
  • That uploader builds bindings with bindingsFromOutput (void/dist/deploy-DsW-PrRQ.mjs ~L1806–1981), whose switch handles text, json, kv, d1, r2, durable-object, queue, ai, browser, images, hyperdrive, worker, workflow, send-email, analytics-engine-dataset, vectorize, dispatch-namespace, assets, version-metadata — but has no ratelimit case, so it hits default: throw new Error(\Cloudflare binding ${name} cannot be uploaded with pinned secrets.`)`.
  • Before the first secret, the normal wrangler versions upload --prebuilt path is used, which supports ratelimits — hence it only breaks once a secret exists.

Expected

Deploy succeeds and preserves existing secrets, as it does for other binding types.

Suggested fix

Add a ratelimit case to bindingsFromOutput mapping to the Workers API binding:

case "ratelimit":
  bindings.push({
    name,
    type: "ratelimit",
    namespace_id: requiredString(value.namespaceId ?? value.namespace_id, name),
    simple: { limit: value.simple.limit, period: value.simple.period },
  });
  break;

(plus a test: Worker with ratelimits + an existing secret deploys via the pinned uploader). It may be worth auditing the switch against all binding types Void can emit (e.g. secrets_store_secrets, mtls_certificates, worker_loaders), and failing in a pre-upload check rather than mid-deploy.

Workaround

Remove ratelimits from the config (or delete all Worker secrets) to deploy.

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

    Fields

    Priority

    None yet

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions