koala73/worldmonitor · error

Input basis must be example or user.

Error message

Input basis must be example or user.

What it means

parseOperationalInput requires input.basis to be exactly the string 'example' or 'user', marking whether the worksheet is the bundled demo or user-authored data. Any other value (other strings, wrong casing, missing field) throws, because downstream logic branches on these two enum values.

Solutions

  1. Set basis to exactly 'example' or 'user' (lowercase) in the input object.
  2. Normalize/whitelist the value at the UI layer before calling the parser (e.g. map the toggle state to these two literals).
  3. For imported JSON, validate value.schema === 'worldmonitor-operational-worksheet/v1' and the basis field before parsing the input.
  4. If migrating old data, map legacy basis values to the two supported literals.

Example fix

// before
calculateOperationalBalance({ ...input, basis: 'demo' });
// after
const basis = input.basis === 'demo' || input.basis === 'template' ? 'example' : input.basis;
calculateOperationalBalance({ ...input, basis });
Defensive patterns

Strategy: validation

Validate before calling

const VALID_BASIS = ['example', 'user'] as const;
if (!VALID_BASIS.includes(input.basis as any)) throw new Error(`basis must be one of ${VALID_BASIS.join(', ')}`);

Type guard

const isBasis = (v: unknown): v is 'example' | 'user' =>
  v === 'example' || v === 'user';

Try / catch

try {
  const snapshot = calculateOperationalBalance(input);
} catch (err) {
  if (err instanceof Error && err.message === 'Input basis must be example or user.') {
    input.basis = 'user'; // safe default for hand-authored data
  } else throw err;
}

Prevention

When it happens

Trigger: Calling parseOperationalInput/calculateOperationalBalance with basis: 'User', 'demo', '', undefined, null, or a JSON export where the basis key was renamed or dropped.

Common situations: Hand-edited worksheet JSON with an invented basis value; a new UI toggle wired to different strings than the parser expects; older exported worksheets from a previous schema version using 'template' instead of 'example'.

Understand the failure class

Background: Invalid enum value errors: "Unknown type", "Invalid scope", "must be one of" — when a string is not on the library's allowed list — this error's family across 23 libraries.

Related errors


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

Appendix: source

Thrown at src/utils/operational-balance.ts:36

  }
  return value;
}
function date(value: unknown): string {
  if (typeof value !== 'string' || !/^\d{4}-\d{2}-\d{2}$/.test(value) || !Number.isFinite(Date.parse(value)) || new Date(value).toISOString().slice(0, 10) !== value || value < '1900-01-01' || value > '9998-12-31') {
    throw new Error('Use a valid date from 1900 through 9998.');
  }
  return value;
}

export function parseOperationalInput(value: unknown): OperationalInput {
  const input = record(value);
  if (typeof input.operation !== 'string' || !input.operation.trim() || input.operation.length > 100) throw new Error('Enter an operation name of 1-100 characters.');
  if (typeof input.unit !== 'string' || !/^[\p{L}\p{N} %./-]{1,24}$/u.test(input.unit) || !input.unit.trim()) throw new Error('Enter one quantity unit of 1-24 characters.');
  const unit = input.unit.trim();
  const startDate = date(input.startDate);
  if (!Number.isInteger(input.horizonDays) || (input.horizonDays as number) < 1 || (input.horizonDays as number) > MAX_OPERATIONAL_DAYS) throw new Error('Horizon must be 1-90 whole days.');
  const horizonDays = input.horizonDays as number;
  if (input.basis !== 'example' && input.basis !== 'user') throw new Error('Input basis must be example or user.');
  const deliveries = (value: unknown): OperationalDelivery[] => {
    if (!Array.isArray(value) || value.length > MAX_OPERATIONAL_DELIVERIES) throw new Error('Use at most 30 deliveries per list.');
    return value.map(value => {
      const row = record(value);
      const deliveryDate = date(row.date);
      if (deliveryDate < startDate || deliveryDate > dateAt(startDate, horizonDays - 1)) throw new Error('Delivery dates must be inside the selected horizon.');
      if (row.unit !== unit) throw new Error(`Every delivery must use ${unit}; mixed units are not converted.`);
      return { date: deliveryDate, unit, quantity: quantity(row.quantity, 'Delivery quantity'), costUsd: row.costUsd === null || row.costUsd === undefined ? null : quantity(row.costUsd, 'Delivery cost') };
    });
  };
  return { operation: input.operation.trim(), basis: input.basis, unit, startDate, horizonDays,
    startingStock: quantity(input.startingStock, 'Starting stock'), dailyDemand: quantity(input.dailyDemand, 'Daily demand'),
    deliveries: deliveries(input.deliveries), alternativeDeliveries: deliveries(input.alternativeDeliveries),
    alternativeDailyDemand: input.alternativeDailyDemand === null ? null : quantity(input.alternativeDailyDemand, 'Alternative daily demand') };
}

export function calculateOperationalBalance(value: unknown): OperationalSnapshot {
  const input = parseOperationalInput(value);

View on GitHub (pinned to 7d06c8633d)