koala73/worldmonitor · error
Expected a worksheet object.
Error message
Expected a worksheet object.
What it means
The operational-balance parser's record() helper validates that the parsed input is a plain object (truthy, typeof object, not an array) before field validation begins. It throws 'Expected a worksheet object.' for anything else, since the worksheet must be an object with named fields.
Solutions
- Wrap the worksheet payload in a plain object with the expected named fields before calling parseOperationalInput.
- Check that the form/request body is not null or an array at the call site.
- Validate the payload shape with a type guard before parsing.
Example fix
// before
parseOperationalInput([{ operation: 'op', unit: 'kg' }]);
// after
parseOperationalInput({ operation: 'op', unit: 'kg', startDate: '2026-01-01', horizonDays: 30, basis: 'user' }); Defensive patterns
Strategy: type-guard
Validate before calling
if (payload === null || typeof payload !== 'object' || Array.isArray(payload)) {
throw new Error('Worksheet payload must be a plain object');
} Type guard
const isWorksheet = (v: unknown): v is Record<string, unknown> => v !== null && typeof v === 'object' && !Array.isArray(v);
Try / catch
try {
const input = parseOperationalInput(raw);
} catch (e) {
if (e instanceof Error && e.message === 'Expected a worksheet object.') {
console.error('Payload is not a plain object; check JSON body shape', e.message);
} else throw e;
} Prevention
- Validate the deserialized JSON body is an object before parsing.
- Ensure forms always serialize to an object, not null or an array.
- Use a schema validator (zod etc.) at the API boundary.
- Handle empty request bodies with an explicit 400 before calling the parser.
When it happens
Trigger: Passing null, undefined, an array (e.g. a JSON array of rows), a string, a number, or a boolean to parseOperationalInput / input() instead of a worksheet object like { operation, unit, startDate, horizonDays, basis, ... }.
Common situations: Submitting an empty form where the payload is null; API clients posting a top-level array of worksheets; JSON body deserialized to a string; spreading a Map instead of converting to an object.
Understand the failure class
Background: Type mismatch errors: IllegalArgumentException, TypeError and type guards across 150 open-source libraries — this error's family across 150 libraries.
Related errors
- ${label} HTTP 400
- Could not resolve ${JSON.stringify(echoCountryInput(raw))} t
- INCOMPATIBLE_DELIVERY
- COUNTRIES_LIMIT_EXCEEDED
- TICKERS_LIMIT_EXCEEDED
AI-assisted analysis of koala73/worldmonitor@7d06c8633d (2026-09-15).
Data as JSON: /api/errors/2845beeb929b7cfa.
Report an issue: GitHub.
Appendix: source
Thrown at src/utils/operational-balance.ts:11
import type { OperationalBalance, OperationalDelivery, OperationalInput, OperationalSnapshot } from '@/types/operational-balance';
export const MAX_OPERATIONAL_DAYS = 90;
export const MAX_OPERATIONAL_DELIVERIES = 30;
export const MAX_OPERATIONAL_JSON_BYTES = 65_536;
const DAY_MS = 86_400_000;
const round = (value: number) => Math.round(value * 1e6) / 1e6;
const dateAt = (start: string, offset: number) => new Date(Date.parse(start) + offset * DAY_MS).toISOString().slice(0, 10);
function record(value: unknown): Record<string, unknown> {
if (!value || typeof value !== 'object' || Array.isArray(value)) throw new Error('Expected a worksheet object.');
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);View on GitHub (pinned to 7d06c8633d)