windmill-labs/windmill · warning

WARNING: '${w.ws}' has an empty rule list — this DELETES ALL

Error message

WARNING: '${w.ws}' has an empty rule list — this DELETES ALL ${w.plan.toDelete.length} backend rule(s) for that workspace.

What it means

`wmill protection-rules push` warns (in red, even with --yes) when a workspace's local rule list is empty while the backend still has rules for that workspace, because pushing would delete all of them. wipesAll marks plans where the push deletes every existing backend rule for a workspace.

Source

Thrown at cli/src/commands/protection-rules/push.ts:174

    }
    if (!opts.dryRun) {
      outputResult(opts, {
        success: true,
        message: "No changes to push - all targeted workspaces are in sync",
      });
    }
    return;
  }

  if (opts.dryRun) {
    if (hadError) process.exit(1);
    return;
  }

  // Pushing an empty list wipes a workspace's rules — be loud even with --yes.
  for (const w of wsPlans) {
    if (w.wipesAll) {
      log.warn(
        colors.red(
          `WARNING: '${w.ws}' has an empty rule list — this DELETES ALL ${w.plan.toDelete.length} backend rule(s) for that workspace.`,
        ),
      );
    }
  }

  const totalDeletes = changed.reduce((n, w) => n + w.plan.toDelete.length, 0);
  if (!opts.yes && !!process.stdin.isTTY) {
    const confirmed = await Confirm.prompt({
      message: totalDeletes > 0
        ? `Apply these changes? This DELETES ${totalDeletes} protection rule(s) across ${changed.length} workspace(s).`
        : `Apply these changes to ${changed.length} workspace(s)?`,
      default: totalDeletes === 0,
    });
    if (!confirmed) {
      log.info("Operation cancelled");
      return;

View on GitHub (pinned to e474e8803c)

Solutions

  1. Abort and run `wmill protection-rules pull` first so the local file reflects backend state.
  2. Re-add the missing rules for that workspace in the local file before pushing.
  3. If the wipe is intentional, rerun with --yes (the warning still shows) — verify toDelete count matches expectations.
  4. Restrict the push to intended workspaces if your file is scoped per workspace.

Example fix

# before: rules.yaml has no rules for ws2
workspaces:
  ws1: [ ... ]
# after: pull then push
wmill protection-rules pull && wmill protection-rules push --yes
Defensive patterns

Strategy: validation

Validate before calling

// abort a wipe-causing push before it happens
import { readFileSync } from 'fs';
const local = parseRules(readFileSync('protection-rules.yaml', 'utf-8'));
for (const ws of workspacesInScope) {
  const backendCount = await fetchBackendRuleCount(ws);
  if ((local[ws] ?? []).length === 0 && backendCount > 0) {
    throw new Error(`refusing push: would delete all ${backendCount} rules for ${ws}; pull first`);
  }
}

Try / catch

// if you must push programmatically, treat the warning as a gate
if (plan.wipesAll && !process.env.CONFIRM_WIPE) {
  throw new Error(`${plan.ws}: push would delete all backend rules — set CONFIRM_WIPE to proceed`);
}

Prevention

When it happens

Trigger: Running push with a local rules file where some workspace has zero rules while that workspace has N > 0 rules on the server; typically the local file only covers some workspaces or rules were never pulled.

Common situations: Fresh checkout with an empty/stale rules file pushed to a configured workspace; editing the YAML and accidentally deleting a workspace's rule block; pushing before running pull to sync.

Related errors


AI-assisted analysis of windmill-labs/windmill@e474e8803c (2026-09-03). Data as JSON: /api/errors/488f98b071cf5c2a. Report an issue: GitHub.