pbakaus/impeccable · error · Error

concept-seed: --reroll must be a non-negative integer

Error message

concept-seed: --reroll must be a non-negative integer

What it means

Thrown when the `reroll` argument is not a non-negative integer (must pass Number.isInteger AND be >= 0; default 0). Reroll selects which deterministic variant the seeded dice produce, so fractional or negative values are meaningless. A common cause is CLI parsing reroll as a string, which fails Number.isInteger.

Source

Thrown at skill/scripts/concept-seed.mjs:310

const SEED_MODES = new Set(['persuade', 'operate', 'read', 'experience']);

export function renderConceptSeed({
  scope = 'surface',
  key = process.env.IMPECCABLE_CONCEPT_SEED || crypto.randomBytes(4).toString('hex'),
  reroll = 0,
  register = null,
  mode = null,
  grain = null,
  platform = null,
  candidateCount = 7,
  catalogDir = CATALOG_DIR,
  _resolvedData = undefined,
} = {}) {
  if (scope !== 'surface' && scope !== 'direction') {
    throw new Error('concept-seed: --scope must be direction or surface');
  }
  if (!Number.isInteger(reroll) || reroll < 0) {
    throw new Error('concept-seed: --reroll must be a non-negative integer');
  }
  if (register !== null && register !== 'safer' && register !== 'bolder') {
    throw new Error('concept-seed: --register must be safer or bolder');
  }
  if (register !== null && reroll < 1) {
    throw new Error('concept-seed: --register steers a re-roll round; pass --reroll <n> with it');
  }
  if (register !== null && scope !== 'direction') {
    throw new Error('concept-seed: --register applies to direction rounds only');
  }
  if (mode !== null && !SEED_MODES.has(mode)) {
    throw new Error('concept-seed: --mode must be persuade, operate, read, or experience');
  }
  // Grain needs no mode: how much of the product is in play is independent of
  // which register of work it is.
  if (grain !== null && !COMPOSITION_GRAINS.includes(grain)) {
    throw new Error(`concept-seed: --grain must be one of ${COMPOSITION_GRAINS.join(', ')}`);
  }

View on GitHub (pinned to d14711ae3d)

Solutions

  1. Coerce and validate reroll to an integer before calling: Number.isInteger(+v) ? +v : ...
  2. Pass 0 for the base round and a positive integer (1, 2, ...) for re-rolls.
  3. Clamp or reject non-integer input at the CLI boundary.

Example fix

// before (CLI forwards a string)
await seedConcepts({ reroll: argv['--reroll'] }); // '2' -> throws

// after
const raw = Number(argv['--reroll'] ?? 0);
await seedConcepts({ reroll: Number.isInteger(raw) && raw >= 0 ? raw : 0 });
Defensive patterns

Strategy: validation

Validate before calling

// Coerce and validate reroll at the CLI boundary.
function parseReroll(raw) {
  const n = Number(raw ?? 0);
  if (!Number.isInteger(n) || n < 0) {
    throw new Error('--reroll must be a non-negative integer');
  }
  return n;
}

Type guard

function isValidReroll(v) {
  return v == null || (Number.isInteger(v) && v >= 0);
}

Prevention

When it happens

Trigger: Passing reroll as a string like '2' from argv without Number() coercion; passing a negative number; passing 1.5 or NaN; passing undefined which then gets mishandled upstream.

Common situations: CLI handler forwarding process.argv values as strings; a caller computing reroll from a formula that can yield a float; passing -1 to mean 'previous'.

Related errors


AI-assisted analysis of pbakaus/impeccable@d14711ae3d (2026-08-13). Data as JSON: /api/errors/adca461ddc6abe05. Report an issue: GitHub.