Hmbown/CodeWhale · error · Error

--attempts must be an integer from 1 to 20

Error message

--attempts must be an integer from 1 to 20

What it means

Thrown by the post-deploy drift gate web/scripts/compare-deployed-facts.mjs when an explicitly passed --attempts value is not an integer in [1, 20]. The value goes through Number(argv[++index]), so a non-numeric token becomes NaN, decimals like 3.5 fail Number.isInteger, and 0 or 21 fail the range check. When the flag is omitted the script defaults to 6 attempts with --require-current and 1 otherwise, so only an explicitly bad value reaches this throw.

Source

Thrown at web/scripts/compare-deployed-facts.mjs:116

async function main() {
  const args = parseArgs(process.argv.slice(2));
  let baseUrl;
  try {
    baseUrl = new URL(args.baseUrl);
  } catch {
    throw new Error(`invalid --base-url: ${args.baseUrl}`);
  }
  if (!/^https?:$/.test(baseUrl.protocol)) {
    throw new Error("--base-url must use http or https");
  }

  const expectedRevision = args.expectedRevision || localRevision();
  if (!expectedRevision || !/^[0-9a-f]{40}$/i.test(expectedRevision)) {
    throw new Error("expected revision must be an exact 40-character Git SHA");
  }
  if (!Number.isInteger(args.attempts) || args.attempts < 1 || args.attempts > 20) {
    throw new Error("--attempts must be an integer from 1 to 20");
  }
  if (!Number.isFinite(args.retryDelayMs) || args.retryDelayMs < 0 || args.retryDelayMs > 30_000) {
    throw new Error("--retry-delay-ms must be between 0 and 30000");
  }

  const facts = buildFacts();
  const expected = {
    sourceRevision: expectedRevision,
    version: facts.version,
    providerCount: facts.providers.length,
    toolCount: facts.toolCount,
    latestPublishedRelease: facts.latestPublishedRelease,
  };
  const endpoint = new URL("/api/facts", baseUrl).toString();
  let last = { error: "not attempted" };
  let differences = [];

  for (let attempt = 1; attempt <= args.attempts; attempt += 1) {

View on GitHub (pinned to 8880682c63)

Solutions

  1. Pass an integer from 1 to 20, for example --attempts 6
  2. Omit the flag entirely; the default is already 6 with --require-current and 1 without
  3. Coerce and clamp sourced values before invoking: Math.min(20, Math.max(1, parseInt(value, 10)))
  4. Check wrapper scripts for a --attempts token whose value was dropped by quoting

Example fix

// before
node web/scripts/compare-deployed-facts.mjs --require-current --attempts 3.5
// after
node web/scripts/compare-deployed-facts.mjs --require-current --attempts 4
Defensive patterns

Strategy: validation

Validate before calling

// validate before spawning the gate script
const raw = Number(process.env.DEPLOY_ATTEMPTS ?? '');
const valid = Number.isInteger(raw) && raw >= 1 && raw <= 20;
const args = ['web/scripts/compare-deployed-facts.mjs'];
if (valid) args.push('--attempts', String(raw)); // otherwise let the script default apply

Prevention

When it happens

Trigger: Running the script with --attempts 0, --attempts 21, --attempts 3.5, --attempts abc (NaN), or a trailing --attempts whose value token is missing so Number(undefined) yields NaN.

Common situations: CI pipelines injecting retry counts from matrix variables or env vars that arrive as floats or empty strings; copying a --tries-style flag from another tool; wrapper scripts quoting away the value token.

Related errors


AI-assisted analysis of Hmbown/CodeWhale@8880682c63 (2026-08-16). Data as JSON: /api/errors/5ffc3ade2016f9eb. Report an issue: GitHub.