You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
workers secrets bulk: --file and unwrapped bodies silently create an empty version + 100% deployment with no error #201
cf workers secrets bulk can silently no-op: it reports success, creates a new Worker version, and even creates a 100% deployment — but with zero binding changes, and no error anywhere. Two distinct paths produce this:
--file sends the request body as application/octet-stream (visible via --dry-run: "bodyKind": "octet-stream"). The secrets-bulk PATCH endpoint apparently cannot parse that body, so the change set is empty.
A bare top-level map body via --body/--body @file, e.g. {"MY_SECRET": {"name": "MY_SECRET", "type": "secret_text", "text": "..."}}. The API contract requires the map to be wrapped in a secrets field — {"secrets": {"MY_SECRET": {...}}} (per the generated CLI help text and the cloudflare TS/Go SDKs, where the params object is {secrets: Record<string, ScriptSecretBulkUpdateParamsSecrets>}). An unwrapped map is silently treated as an empty merge patch.
Both paths return success: true with the current bindings manifest as the result, and both create a new version annotated workers/triggered_by: secret plus a 100% deployment. Operators who trust the exit code (0) or the success response will believe the secret landed when it did not.
Repro
# 1. --file path (octet-stream, silent no-op)echo'{"MY_SECRET": {"name": "MY_SECRET", "type": "secret_text", "text": "value"}}'> body.json
cf workers secrets bulk --worker my-worker --file body.json
# -> success, new version created, MY_SECRET absent from the version's bindings# 2. unwrapped --body (silent no-op)
cf workers secrets bulk --worker my-worker --body @body.json
# -> same result# 3. wrapped --body (works)echo'{"secrets": {"MY_SECRET": {"name": "MY_SECRET", "type": "secret_text", "text": "value"}}}'> body2.json
cf workers secrets bulk --worker my-worker --body @body2.json
# -> MY_SECRET present in the new version's bindings
Verify with cf workers versions list --worker-id my-worker (inspect the new version's bindings) or cf workers secrets get MY_SECRET --worker my-worker.
Expected behaviour
--file should deliver the file contents as a JSON request body (the same body --body @file produces), or the flag should be rejected for commands whose body is JSON.
A body that does not match the documented shape (missing secrets wrapper) should fail loudly — a client-side validation error or a surfaced API error — rather than being treated as an empty merge patch.
At minimum, the command should print a warning when the resulting change set is empty, since it creates a version + deployment as a side effect.
Versions
cf CLI: v1.0.0-beta.6 (delegated)
macOS arm64, observed 2026-10-04 against the workers secrets bulk command (PATCH /accounts/{account_id}/workers/scripts/{script_name}/secrets-bulk).
Hit the same thing on 1.0.0-beta.12 (2026-10-20), on a real Worker that already had one secret and a few plain_text vars. A few details that aren't in this issue or #102 yet.
Exit 0, the API answers 200, a new version (triggered_by: secret) and a 100% deployment show up, and no binding changes. A new secret is not created. An existing secret is not updated. The same body wrapped in {"secrets": {...}} applies fine.
Things I checked on top of that:
A body that isn't valid JSON gets a 400. So the endpoint does read the body, and a flat patch is parsed and then thrown away.
--dry-run prints the PATCH to .../secrets-bulk with the body exactly as given, so it looks right. It doesn't validate the shape, so it can't be used to catch this before sending.
cf workers secrets bulk --help says secrets not in the request are left unchanged (merge patch), but never says the body needs the secrets key. cf schema lists no body fields for this operation.
Impact for me: a rotation script written from the help text reports success and ships a no-op, and every attempt burns a version.
What would help: reject an unknown top-level shape with a 4xx or a CLI error, or at least show the secrets wrapper in the help and the schema.
For the record, #96 is a different symptom (secrets update replacing the whole set), so I don't think it's the right home for this. #102 and this issue cover the same silent no-op, so I didn't open a third one.
Activity
tuha263 commentedon Oct 20, 2026
Hit the same thing on 1.0.0-beta.12 (2026-10-20), on a real Worker that already had one secret and a few plain_text vars. A few details that aren't in this issue or #102 yet.
Flat body,
cf workers secrets bulk --worker my-worker --file body.json:Exit 0, the API answers 200, a new version (triggered_by: secret) and a 100% deployment show up, and no binding changes. A new secret is not created. An existing secret is not updated. The same body wrapped in
{"secrets": {...}}applies fine.Things I checked on top of that:
--dry-runprints the PATCH to .../secrets-bulk with the body exactly as given, so it looks right. It doesn't validate the shape, so it can't be used to catch this before sending.cf workers secrets bulk --helpsays secrets not in the request are left unchanged (merge patch), but never says the body needs thesecretskey.cf schemalists no body fields for this operation.Impact for me: a rotation script written from the help text reports success and ships a no-op, and every attempt burns a version.
What would help: reject an unknown top-level shape with a 4xx or a CLI error, or at least show the
secretswrapper in the help and the schema.For the record, #96 is a different symptom (
secrets updatereplacing the whole set), so I don't think it's the right home for this. #102 and this issue cover the same silent no-op, so I didn't open a third one.