koala73/worldmonitor · error
Enter an operation name of 1-100 characters.
Error message
Enter an operation name of 1-100 characters.
What it means
parseOperationalInput validates the operation name as a non-empty trimmed string of at most 100 characters; whitespace-only names are rejected via input.operation.trim(). It throws 'Enter an operation name of 1-100 characters.' when the field is missing, not a string, blank, or longer than 100 characters.
Solutions
- Provide a non-empty operation string, trimmed, at most 100 characters.
- Trim and check length in the form before submission: op.trim().length >= 1 && op.length <= 100.
- Map alternate field names (title/name) to operation at the API boundary.
Example fix
// before
parseOperationalInput({ operation: ' ', ... });
// after
parseOperationalInput({ operation: 'Operation Desert Resupply'.slice(0, 100), ... }); Defensive patterns
Strategy: validation
Validate before calling
const op = (payload as any)?.operation;
if (typeof op !== 'string' || !op.trim() || op.length > 100) {
throw new Error('Operation name must be 1-100 non-blank characters');
} Type guard
const hasValidOperation = (w: unknown): w is { operation: string } & Record<string, unknown> =>
w !== null && typeof w === 'object' && typeof (w as any).operation === 'string' &&
(w as any).operation.trim().length > 0 && (w as any).operation.length <= 100; Try / catch
try {
const input = parseOperationalInput(raw);
} catch (e) {
if (e instanceof Error && e.message.includes('operation name of 1-100')) {
console.error('Operation name missing, blank, or over 100 chars:', e.message);
} else throw e;
} Prevention
- Make the operation field required and non-blank in the form.
- Trim and length-check on input events, not only at submit.
- Map alternate field names (title/name) to operation at the boundary.
- Set maxlength="100" on the operation input.
When it happens
Trigger: Omitting the operation field, sending null/undefined or a number, submitting a form with only spaces, or pasting a description longer than 100 characters into the operation field.
Common situations: Form autosave persisting an empty name; API consumers using a different field name (e.g. title or name) so operation is undefined; copy-pasted long operation descriptions from planning documents.
Understand the failure class
Background: "must not be empty", "cannot be empty" — required-field validation errors across open-source libraries — this error's family across 41 libraries.
Related errors
- callbackUrl is required
- Name is required
- Company is required
- get-intel-timeline: domain Required unless country is set. O
- ${label} HTTP 400
AI-assisted analysis of koala73/worldmonitor@7d06c8633d (2026-09-15).
Data as JSON: /api/errors/5ff03148c6f8bd27.
Report an issue: GitHub.
Appendix: source
Thrown at src/utils/operational-balance.ts:30
return value as Record<string, unknown>;
}
function quantity(value: unknown, label: string): number {
if (value === '' || value === null || value === undefined) throw new Error(`${label} is required.`);
if (typeof value !== 'number' || !Number.isFinite(value) || value < 0 || value > 1e6 || round(value) !== value) {
throw new Error(`${label} must be between 0 and 1 million with at most 6 decimal places.`);
}
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'),View on GitHub (pinned to 7d06c8633d)