koala73/worldmonitor · error · Error

batch ordinals must be contiguous from 0

Error message

batch ordinals must be contiguous from 0

What it means

Thrown by normalizeCompanyImportBatch() when the sorted rows' ordinal values do not form an exact 0-based sequence. After sorting by ordinal, each row at index i must have ordinal === i, which rejects gaps, duplicates, and 1-based numbering alike.

Source

Thrown at shared/company-monitoring-contract.ts:319

  if (utf8Bytes(JSON.stringify(normalized)) > COMPANY_MONITORING_LIMITS.maxImportRowBytes) {
    throw new Error(`row exceeds ${COMPANY_MONITORING_LIMITS.maxImportRowBytes} bytes`);
  }
  return normalized;
}

export function normalizeCompanyImportBatch(inputs: CompanyImportRowInput[]): NormalizedCompanyImportRow[] {
  if (!Array.isArray(inputs)) throw new Error('import batch must be a list');
  if (inputs.length === 0) throw new Error('import batch requires at least one row');
  if (inputs.length > COMPANY_MONITORING_LIMITS.maxImportRows) {
    throw new Error(`batch exceeds ${COMPANY_MONITORING_LIMITS.maxImportRows} rows`);
  }

  const normalized = inputs.map(normalizeCompanyImportRow).sort((left, right) => left.ordinal - right.ordinal);
  const importId = normalized[0]?.clientImportId;
  for (let index = 0; index < normalized.length; index += 1) {
    const row = normalized[index]!;
    if (row.clientImportId !== importId) throw new Error('batch rows must share one clientImportId');
    if (row.ordinal !== index) throw new Error('batch ordinals must be contiguous from 0');
  }
  if (utf8Bytes(JSON.stringify(normalized)) > COMPANY_MONITORING_LIMITS.maxImportBatchBytes) {
    throw new Error(`batch exceeds ${COMPANY_MONITORING_LIMITS.maxImportBatchBytes} bytes`);
  }
  return normalized;
}

export function assertCompanyMonitoringPayloadSize(byteLength: number): void {
  if (!Number.isSafeInteger(byteLength) || byteLength < 0) throw new Error('request byte length is invalid');
  if (byteLength > COMPANY_MONITORING_LIMITS.maxRequestBytes) {
    throw new Error(`request exceeds ${COMPANY_MONITORING_LIMITS.maxRequestBytes} bytes`);
  }
}

export function assertCompanyMonitoringAccountContext(
  context: { ownerAccountId?: string } | null | undefined,
): string {
  const ownerAccountId = context?.ownerAccountId;

View on GitHub (pinned to eeab0a219f)

Solutions

  1. Renumber the batch 0..n-1 immediately before calling normalizeCompanyImportBatch(), after any filtering or deletion
  2. If ordinals arrive 1-based from user data, subtract 1 during row normalization
  3. Pre-validate: rows.every((r, i) => r.ordinal === i) after sorting by ordinal, and fail fast in the UI with the offending index

Example fix

// before
const rows = csvLines.map((line, i) => ({ ...line, ordinal: i + 1 })); // 1-based from spreadsheet
const normalized = normalizeCompanyImportBatch(rows); // throws: ordinals must be contiguous from 0

// after
const rows = csvLines.map((line, i) => ({ ...line, ordinal: i })); // 0-based, contiguous
const normalized = normalizeCompanyImportBatch(rows);
Defensive patterns

Strategy: validation

Validate before calling

const rows = rawRows
  .filter(isValidRow)
  .sort((a, b) => a.ordinal - b.ordinal)
  .map((r, i) => ({ ...r, ordinal: i })); // resequence 0..n-1 after filter+sort
if (!rows.every((r, i) => r.ordinal === i)) throw new Error('ordinal resequencing failed');

Type guard

function hasContiguousOrdinals(rows: { ordinal: number }[]): boolean {
  const sorted = [...rows].sort((a, b) => a.ordinal - b.ordinal);
  return sorted.every((r, i) => r.ordinal === i);
}

Try / catch

catch (e) { if (e instanceof Error && e.message === 'batch ordinals must be contiguous from 0') resequenceAndRetry(); else throw e; }

Prevention

When it happens

Trigger: Sending ordinals 1..N from user-facing or spreadsheet data that is 1-based; filtering out invalid rows client-side but not renumbering the survivors, leaving gaps; duplicate ordinals from a mapping bug; an ordinal >= maxImportRows (100) surviving the earlier per-row check.

Common situations: CSV importers that pass through the file's row number as ordinal; UI code that deletes a row from a draft batch without resequencing; two async workers appending rows with overlapping counters.

Understand the failure class

Background: Schema validation failed / invalid input schema: payload rejected because its shape doesn't match the expected schema — this error's family across 28 libraries.

Related errors


AI-assisted analysis of koala73/worldmonitor@eeab0a219f (2026-08-21). Data as JSON: /api/errors/995f6554f4776d12. Report an issue: GitHub.