koala73/worldmonitor · error · Error

exactly one of CLOUDFLARE_API_TOKEN or CLOUDFLARE_ALL_ACCESS

Error message

exactly one of CLOUDFLARE_API_TOKEN or CLOUDFLARE_ALL_ACCESS_TOKEN is required for --check and --apply

What it means

resolveToken reads CLOUDFLARE_API_TOKEN and CLOUDFLARE_ALL_ACCESS_TOKEN from the environment and requires exactly one to be set. When neither is set it throws this message (when both are set it throws a different 'not both' message). The token is required to authenticate --check and --apply runs against the Cloudflare API.

Solutions

  1. Export CLOUDFLARE_API_TOKEN (or CLOUDFLARE_ALL_ACCESS_TOKEN) with a valid scoped token before running.
  2. Load your credentials file via the repo's loadEnvFile() mechanism or source it in the shell.
  3. In CI, add the secret and expose it as the exact environment variable name.
  4. Verify with `echo ${CLOUDFLARE_API_TOKEN:+set}` that exactly one variable is present.

Example fix

// before
node scripts/cloudflare-cache-rule.mjs --check   // no token in env
// after
export CLOUDFLARE_API_TOKEN=cf_your_token
node scripts/cloudflare-cache-rule.mjs --check
Defensive patterns

Strategy: validation

Validate before calling

const tokens = [process.env.CLOUDFLARE_API_TOKEN, process.env.CLOUDFLARE_ALL_ACCESS_TOKEN].filter(Boolean);
if (tokens.length !== 1) {
  throw new Error('export exactly one of CLOUDFLARE_API_TOKEN or CLOUDFLARE_ALL_ACCESS_TOKEN before running');
}

Type guard

function hasCloudflareToken(env = process.env) {
  return [env.CLOUDFLARE_API_TOKEN, env.CLOUDFLARE_ALL_ACCESS_TOKEN].filter(Boolean).length === 1;
}

Try / catch

try {
  const token = resolveToken(env);
} catch (e) {
  if (e.message.includes('is required for --check and --apply')) {
    console.error('Missing Cloudflare token. Set CLOUDFLARE_API_TOKEN (or CLOUDFLARE_ALL_ACCESS_TOKEN) in the environment or via loadEnvFile().');
    process.exit(2);
  }
  throw e;
}

Prevention

When it happens

Trigger: Running the script with --check or --apply in an environment where both token variables are unset — e.g. a CI job without secrets, a shell that never sourced the env file, or calling resolveToken with a custom env object lacking the keys.

Common situations: Fresh clone where the local .env was never loaded, GitHub Actions secrets not mapped into env, renamed/typo'd variable (CLOUDFLARE_TOKEN), or switching machines.

Understand the failure class

Background: "environment variable is not set" and "Missing keys in environment" errors: what missing required env var messages mean and how to fix them — this error's family across 28 libraries.

Related errors


AI-assisted analysis of koala73/worldmonitor@7d06c8633d (2026-09-15). Data as JSON: /api/errors/1765e6583d9b3ffc. Report an issue: GitHub.

Appendix: source

Thrown at scripts/cloudflare-cache-rule.mjs:536

      token,
      fetchImpl,
    });
    if (zone?.name !== ZONE_NAME) {
      throw new Error(
        `CLOUDFLARE_ZONE_ID ${env.CLOUDFLARE_ZONE_ID} is zone "${zone?.name ?? 'unknown'}", not ${ZONE_NAME}`,
      );
    }
    return zone.id;
  }
  const zones = await cloudflareRequest(`/zones?name=${encodeURIComponent(ZONE_NAME)}`, { token, fetchImpl });
  const zone = zones?.[0];
  if (!zone) throw new Error(`no Cloudflare zone named ${ZONE_NAME} is visible to this token`);
  return zone.id;
}

export function resolveToken(env = process.env) {
  const tokens = [env.CLOUDFLARE_API_TOKEN, env.CLOUDFLARE_ALL_ACCESS_TOKEN].filter(Boolean);
  if (tokens.length !== 1) {
    throw new Error(
      tokens.length
        ? 'set exactly one of CLOUDFLARE_API_TOKEN or CLOUDFLARE_ALL_ACCESS_TOKEN, not both'
        : 'exactly one of CLOUDFLARE_API_TOKEN or CLOUDFLARE_ALL_ACCESS_TOKEN is required for --check and --apply',
    );
  }
  return tokens[0];
}

/**
 * Whether a live rule carries a ref somebody chose, as opposed to Cloudflare's
 * default. Cloudflare fills an unset `ref` with the rule's own id, and — learned
 * from the live zone while landing #7747 — refuses to change it afterwards: a
 * PATCH that sends a new `ref` for such a rule fails with error 20142, "expected
 * the reference to be empty". A ref is only ever accepted at creation. So a rule
 * adopted by description keeps its default ref for life, and a default ref is
 * identity to adopt, never drift to repair.
 */

View on GitHub (pinned to 7d06c8633d)