{"record":{"id":"488f98b071cf5c2a","repo":"windmill-labs/windmill","slug":"warning-w-ws-has-an-empty-rule-list-this-d","errorCode":null,"errorMessage":"WARNING: '${w.ws}' has an empty rule list — this DELETES ALL ${w.plan.toDelete.length} backend rule(s) for that workspace.","messagePattern":"WARNING: '(.+?)' has an empty rule list — this DELETES ALL (.+?) backend rule\\(s\\) for that workspace\\.","errorType":"console","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"cli/src/commands/protection-rules/push.ts","lineNumber":174,"sourceCode":"    }\n    if (!opts.dryRun) {\n      outputResult(opts, {\n        success: true,\n        message: \"No changes to push - all targeted workspaces are in sync\",\n      });\n    }\n    return;\n  }\n\n  if (opts.dryRun) {\n    if (hadError) process.exit(1);\n    return;\n  }\n\n  // Pushing an empty list wipes a workspace's rules — be loud even with --yes.\n  for (const w of wsPlans) {\n    if (w.wipesAll) {\n      log.warn(\n        colors.red(\n          `WARNING: '${w.ws}' has an empty rule list — this DELETES ALL ${w.plan.toDelete.length} backend rule(s) for that workspace.`,\n        ),\n      );\n    }\n  }\n\n  const totalDeletes = changed.reduce((n, w) => n + w.plan.toDelete.length, 0);\n  if (!opts.yes && !!process.stdin.isTTY) {\n    const confirmed = await Confirm.prompt({\n      message: totalDeletes > 0\n        ? `Apply these changes? This DELETES ${totalDeletes} protection rule(s) across ${changed.length} workspace(s).`\n        : `Apply these changes to ${changed.length} workspace(s)?`,\n      default: totalDeletes === 0,\n    });\n    if (!confirmed) {\n      log.info(\"Operation cancelled\");\n      return;","sourceCodeStart":156,"sourceCodeEnd":192,"githubUrl":"https://github.com/windmill-labs/windmill/blob/e474e8803ce2ff5c2df09a58dab51d45f5c922ca/cli/src/commands/protection-rules/push.ts#L156-L192","documentation":"`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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Abort and run `wmill protection-rules pull` first so the local file reflects backend state.","Re-add the missing rules for that workspace in the local file before pushing.","If the wipe is intentional, rerun with --yes (the warning still shows) — verify toDelete count matches expectations.","Restrict the push to intended workspaces if your file is scoped per workspace."],"exampleFix":"# before: rules.yaml has no rules for ws2\nworkspaces:\n  ws1: [ ... ]\n# after: pull then push\nwmill protection-rules pull && wmill protection-rules push --yes","handlingStrategy":"validation","validationCode":"// abort a wipe-causing push before it happens\nimport { readFileSync } from 'fs';\nconst local = parseRules(readFileSync('protection-rules.yaml', 'utf-8'));\nfor (const ws of workspacesInScope) {\n  const backendCount = await fetchBackendRuleCount(ws);\n  if ((local[ws] ?? []).length === 0 && backendCount > 0) {\n    throw new Error(`refusing push: would delete all ${backendCount} rules for ${ws}; pull first`);\n  }\n}","typeGuard":null,"tryCatchPattern":"// if you must push programmatically, treat the warning as a gate\nif (plan.wipesAll && !process.env.CONFIRM_WIPE) {\n  throw new Error(`${plan.ws}: push would delete all backend rules — set CONFIRM_WIPE to proceed`);\n}","preventionTips":["Always `pull` before `push` when the local file may be stale.","Never hand-delete a whole workspace block from the rules file without checking the backend.","Review the WARNING line even when using --yes.","Scope pushes to the workspaces you actually edited."],"tags":["cli","protection-rules","destructive-operation","sync"],"backgroundTag":"destructive-push-empty-state","analyzedSha":"e474e8803ce2ff5c2df09a58dab51d45f5c922ca","analyzedAt":"2026-09-03T12:38:19.024Z","contentChangedAt":"2026-09-03T12:38:19.024Z","schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}