Skip to content

workers secrets bulk: --file and unwrapped bodies silently create an empty version + 100% deployment with no error #201

Open

Description

@cameronm123

What happened

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:

  1. --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.
  2. 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).

Activity

  1. tuha263 commented on Oct 20, 2026

    @tuha263

    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:

    {"MY_SECRET":{"type":"secret_text","text":"value"}}
    

    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.

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

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions