koala73/worldmonitor · error · Error
Expected one enabled block rule named ${description}
Error message
Expected one enabled block rule named ${description} What it means
planAgentReadiness expects the Cloudflare firewall ruleset to contain exactly one rule per description in BLOCK_RULES, and that rule must be an enabled block rule. It throws when the count of rules matching a description is not 1, or the single match is not action 'block' or is disabled. This guards against duplicate or drifted rules before planning changes.
Solutions
- Open the Cloudflare dashboard (or GET the phase entrypoint ruleset) and inspect rules matching the description; delete duplicates so exactly one remains.
- Re-enable the rule and/or set its action back to 'block' if it was disabled or changed.
- If no rule exists, create the expected block rule (or run the setup path) before running planAgentReadiness.
- Re-run with --plan after the ruleset matches the expected one-rule-per-description shape.
Defensive patterns
Strategy: try-catch
Validate before calling
const descriptions = BLOCK_RULES; // import from the script if exposed
const grouped = Object.groupBy(firewall.rules, (r) => r.description);
for (const d of descriptions) {
const m = grouped[d] ?? [];
if (m.length !== 1 || m[0].action !== 'block' || m[0].enabled === false) {
throw new Error(`ruleset drift on rule "${d}"; reconcile before planning`);
}
}
planAgentReadiness(firewall); Try / catch
try {
const changes = planAgentReadiness(firewall);
} catch (e) {
if (e.message.startsWith('Expected one enabled block rule named')) {
console.error('Ruleset drifted from expected shape; inspect and reconcile in the dashboard.');
process.exitCode = 2;
return;
}
throw e;
} Prevention
- Manage these rules exclusively through the script; forbid manual dashboard edits.
- Run --check in CI to detect drift early.
- Use audit logs to find who duplicated/disabled rules.
- Keep apply runs serialized (single pipeline, no parallel invocations).
When it happens
Trigger: The zone's firewall phase has zero rules with a BLOCK_RULES description, two or more rules share the same description (e.g. from concurrent manual edits or double-apply), or the matching rule has action changed to something other than 'block' or enabled set to false.
Common situations: Someone manually edited or disabled the block rule in the Cloudflare dashboard, the apply script was run twice creating duplicates, or the ruleset was rebuilt from a different config losing the rule.
Understand the failure class
Background: Schema validation failed / invalid input schema: payload rejected because its shape doesn't match the expected schema — this error's family across 28 libraries.
Related errors
- DNS ${recordType} lookup failed: HTTP ${response.status}
- Use --plan, --check, or --apply
- Cloudflare rules changed after planning. Run --plan again be
- Cloudflare verification failed after apply
- Cloudflare ${method} ${path} did not complete (a write may s
AI-assisted analysis of koala73/worldmonitor@7d06c8633d (2026-09-15).
Data as JSON: /api/errors/577657392fb48c4d.
Report an issue: GitHub.
Appendix: source
Thrown at scripts/cloudflare-agent-readiness.mjs:23
import { cloudflareRequest, resolveToken, resolveZoneId } from './cloudflare-cache-rule.mjs';
const FIREWALL_PHASE = 'http_request_firewall_custom';
const BLOCK_RULES = ['Block API Bots', 'Block Scriptlike UAs'];
// This script owns the firewall half of agent readiness: the JSON block
// responses on the two bot rules. The cache half — keeping the declared AI
// agents off the shared HTML entry for `/` so middleware.ts can hand them
// /home.md — lives in scripts/cloudflare-cache-rule.mjs since #7804, as a
// carve-out inside the one managed document rule. The UA-keyed bypass this
// script used to append LAST in the cache phase is retired there
// (RETIRED_CACHE_RULES): two scripts each insisting on the last position would
// have moved each other's rule on every run.
export function planAgentReadiness(firewall) {
const changes = [];
for (const description of BLOCK_RULES) {
const matches = firewall.rules.filter((rule) => rule.description === description);
if (matches.length !== 1 || matches[0].action !== 'block' || matches[0].enabled === false) {
throw new Error(`Expected one enabled block rule named ${description}`);
}
const rule = matches[0];
const response = {
status_code: 403,
content_type: 'application/json',
content: JSON.stringify(policy.blockedResponse),
};
if (!isDeepStrictEqual(rule.action_parameters?.response, response)) {
const definition = Object.fromEntries(Object.entries(rule).filter(([key]) =>
!['id', 'version', 'last_updated'].includes(key)));
changes.push({
phase: FIREWALL_PHASE, rulesetId: firewall.id, ruleId: rule.id,
description, method: 'PATCH',
body: { ...definition, action_parameters: { ...rule.action_parameters, response } },
});
}
}
View on GitHub (pinned to 7d06c8633d)