{"record":{"id":"2845beeb929b7cfa","repo":"koala73/worldmonitor","slug":"expected-a-worksheet-object","errorCode":null,"errorMessage":"Expected a worksheet object.","messagePattern":"Expected a worksheet object\\.","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/utils/operational-balance.ts","lineNumber":11,"sourceCode":"import type { OperationalBalance, OperationalDelivery, OperationalInput, OperationalSnapshot } from '@/types/operational-balance';\n\nexport const MAX_OPERATIONAL_DAYS = 90;\nexport const MAX_OPERATIONAL_DELIVERIES = 30;\nexport const MAX_OPERATIONAL_JSON_BYTES = 65_536;\nconst DAY_MS = 86_400_000;\nconst round = (value: number) => Math.round(value * 1e6) / 1e6;\nconst dateAt = (start: string, offset: number) => new Date(Date.parse(start) + offset * DAY_MS).toISOString().slice(0, 10);\n\nfunction record(value: unknown): Record<string, unknown> {\n  if (!value || typeof value !== 'object' || Array.isArray(value)) throw new Error('Expected a worksheet object.');\n  return value as Record<string, unknown>;\n}\nfunction quantity(value: unknown, label: string): number {\n  if (value === '' || value === null || value === undefined) throw new Error(`${label} is required.`);\n  if (typeof value !== 'number' || !Number.isFinite(value) || value < 0 || value > 1e6 || round(value) !== value) {\n    throw new Error(`${label} must be between 0 and 1 million with at most 6 decimal places.`);\n  }\n  return value;\n}\nfunction date(value: unknown): string {\n  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') {\n    throw new Error('Use a valid date from 1900 through 9998.');\n  }\n  return value;\n}\n\nexport function parseOperationalInput(value: unknown): OperationalInput {\n  const input = record(value);","sourceCodeStart":1,"sourceCodeEnd":29,"githubUrl":"https://github.com/koala73/worldmonitor/blob/7d06c8633d256c18e38133030bc3613976a96ec9/src/utils/operational-balance.ts#L1-L29","documentation":"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.","triggerScenarios":"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, ... }.","commonSituations":"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.","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."],"exampleFix":"// before\nparseOperationalInput([{ operation: 'op', unit: 'kg' }]);\n// after\nparseOperationalInput({ operation: 'op', unit: 'kg', startDate: '2026-01-01', horizonDays: 30, basis: 'user' });","handlingStrategy":"type-guard","validationCode":"if (payload === null || typeof payload !== 'object' || Array.isArray(payload)) {\n  throw new Error('Worksheet payload must be a plain object');\n}","typeGuard":"const isWorksheet = (v: unknown): v is Record<string, unknown> =>\n  v !== null && typeof v === 'object' && !Array.isArray(v);","tryCatchPattern":"try {\n  const input = parseOperationalInput(raw);\n} catch (e) {\n  if (e instanceof Error && e.message === 'Expected a worksheet object.') {\n    console.error('Payload is not a plain object; check JSON body shape', e.message);\n  } else throw e;\n}","preventionTips":["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."],"tags":["validation","type-check","parsing"],"backgroundTag":"type-mismatch","analyzedSha":"7d06c8633d256c18e38133030bc3613976a96ec9","analyzedAt":"2026-09-15T16:44:39.439Z","contentChangedAt":"2026-09-15T16:44:39.439Z","schemaVersion":2},"datasetVersion":"2026-09-15T18:17:12.389Z"}