koala73/worldmonitor · error · Error
Cloudflare verification failed after apply
Error message
Cloudflare verification failed after apply
What it means
After applying all planned changes, runAgentReadiness re-plans against a fresh read of the firewall phase to verify convergence. If any change is still pending — meaning an apply write did not take effect or a rule regressed — it throws 'Cloudflare verification failed after apply'. This is the post-condition check that the zone is actually ready.
Solutions
- Wait briefly and re-run --check to see if the ruleset settled (propagation delay).
- Inspect the phase entrypoint ruleset and compare each rule against the expected block-rule policy to find which rule did not stick.
- Check audit logs for other actors that may have reverted the rule right after apply.
- Re-run --plan then --apply once the interfering writer is stopped.
Defensive patterns
Strategy: retry
Validate before calling
const remaining = planAgentReadiness(await readFirewall(token));
if (remaining.length) {
console.error('apply did not converge; pending changes:', remaining.map((c) => c.description));
} Try / catch
try {
await runAgentReadiness('--apply', { env, fetchImpl });
} catch (e) {
if (e.message === 'Cloudflare verification failed after apply') {
await sleep(5000); // allow propagation to settle
await runAgentReadiness('--apply', { env, fetchImpl });
return;
}
throw e;
} Prevention
- Add a bounded retry with backoff around the apply step for convergence lag.
- Check Cloudflare audit logs for other actors reverting rules.
- Alert on repeated verification failures — that signals an interfering automation, not lag.
- Run --check a few minutes after apply in CI as a follow-up verification.
When it happens
Trigger: An HTTP update returned success but the resulting ruleset still leaves a BLOCK_RULES entry missing, non-block, or disabled, so planAgentReadiness on the re-read returns non-empty changes.
Common situations: Cloudflare propagation lag or an eventually-consistent read right after write, a middleware/other automation immediately reverting the rule, or the update API accepting the request while partially applying it.
Related errors
- DNS ${recordType} lookup failed: HTTP ${response.status}
- ${pagePath} is missing its recent-developments section
- Expected one enabled block rule named ${description}
- Use --plan, --check, or --apply
- Cloudflare rules changed after planning. Run --plan again be
AI-assisted analysis of koala73/worldmonitor@7d06c8633d (2026-09-15).
Data as JSON: /api/errors/b0bf6d19b61ec49b.
Report an issue: GitHub.
Appendix: source
Thrown at scripts/cloudflare-agent-readiness.mjs:65
const token = resolveToken(env);
const zoneId = await resolveZoneId(token, { env, fetchImpl });
const read = (phase) => cloudflareRequest(`/zones/${zoneId}/rulesets/phases/${phase}/entrypoint`, { token, fetchImpl });
const firewall = await read(FIREWALL_PHASE);
const changes = planAgentReadiness(firewall);
if (mode !== '--apply' || changes.length === 0) return { zone: 'worldmonitor.app', ready: changes.length === 0, changes };
for (const change of changes) {
const current = await read(change.phase);
if (!isDeepStrictEqual(current.rules, firewall.rules)) {
throw new Error('Cloudflare rules changed after planning. Run --plan again before applying.');
}
const updated = await cloudflareRequest(`/zones/${zoneId}/rulesets/${change.rulesetId}/rules/${change.ruleId}`, {
token, fetchImpl, method: change.method, body: change.body,
});
firewall.rules = updated.rules;
}
const remaining = planAgentReadiness(await read(FIREWALL_PHASE));
if (remaining.length) throw new Error('Cloudflare verification failed after apply');
return { zone: 'worldmonitor.app', ready: true, applied: changes.map((change) => change.description) };
}
if (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href) {
const mode = process.argv[2];
if (process.argv.length !== 3 || !['--plan', '--check', '--apply'].includes(mode)) {
console.error('Usage: node scripts/cloudflare-agent-readiness.mjs --plan|--check|--apply');
process.exitCode = 1;
} else {
loadEnvFile(import.meta.url, { only: ['CLOUDFLARE_API_TOKEN', 'CLOUDFLARE_ALL_ACCESS_TOKEN', 'CLOUDFLARE_ZONE_ID'] });
runAgentReadiness(mode).then((result) => {
console.log(JSON.stringify(result, null, 2));
if (mode === '--check' && !result.ready) process.exitCode = 1;
}).catch((error) => {
console.error(error.message);
process.exitCode = 1;
});
}View on GitHub (pinned to 7d06c8633d)