ChronousDocumentation

API

Errors

The four error classes, what throws them and where they are caught for you.

The four classes

ClassTypeDescription
InvalidEventErrorErrorAn event that cannot be read, or one that ends before it starts. Carries the offending eventId.
InvalidRangeErrorErrorAn anchor date or time zone that cannot be read, a slotMinutes outside 1 to 1440, or a dayCount below one.
InvalidRecurrenceErrorErrorA rule part the engine does not support, rather than ignoring it.
MissingTemporalErrorErrorReported immediately when globalThis.Temporal is absent. Chronous reads Temporal synchronously.
tsx
import { InvalidEventError, buildCalendar } from '@midstem/chronous'
import type { EventInput } from '@midstem/chronous'

const events: EventInput[] = [
  { id: 'standup', start: '2026-03-18T09:00', duration: 'PT30M' },
  // ends before it starts
  { id: 'broken', start: '2026-03-18T15:00', end: '2026-03-18T14:00' },
]

try {
  buildCalendar(
    { view: 'day', currentDate: '2026-03-18', timeZone: 'Europe/Kyiv' },
    events,
  )
} catch (error) {
  if (error instanceof InvalidEventError) {
    console.log(error.eventId) // 'broken'
  }
}
// One unusable event fails the whole call — nothing is drawn from half a list.

In React they are caught for you

useCalendar catches all four and hands them back as state, because a throw during render takes the whole tree down.

tsx
import { useCalendar } from '@midstem/chronous-react'
import type { CalendarRange, EventInput } from '@midstem/chronous-react'

// An unreadable time zone: InvalidRangeError rather than a bare RangeError
const range: CalendarRange = {
  view: 'week',
  currentDate: '2026-03-18',
  timeZone: 'Europe/Kyviv',
}

const events: EventInput[] = [
  { id: 'standup', start: '2026-03-18T09:00', duration: 'PT30M' },
]

export const Board = () => {
  const { calendar, error } = useCalendar(range, events)

  // calendar is null exactly when error is set
  if (error) return <p className="failed">{error.message}</p>

  return <p>{calendar.days.length} days drawn</p>
}

Calendar.Root renders renderError inside its own element, so the layout does not collapse — and rethrows when no renderError is given, because an invalid range is a bug in the input and swallowing it into a blank grid hides it.

tsx
import { Calendar } from '@midstem/chronous-react'
import type {
  CalendarError,
  CalendarRange,
  EventInput,
} from '@midstem/chronous-react'

const range: CalendarRange = {
  view: 'week',
  currentDate: '2026-03-18',
  timeZone: 'Europe/Kyiv',
}

const events: EventInput[] = [
  { id: 'standup', start: '2026-03-18T09:00', duration: 'PT30M' },
]

const Failed = ({ error }: { error: CalendarError }) => (
  <p className="failed">{error.message}</p>
)

export const Board = () => (
  <Calendar.Root
    className="calendar"
    range={range}
    events={events}
    // Without this, an unreadable range rethrows rather than drawing
    // a blank grid that hides the bug.
    renderError={error => <Failed error={error} />}
  >
    <Calendar.TimeGrid hourHeight={48}>
      <Calendar.DayColumns className="column">
        <Calendar.TimedEvents className="event" />
      </Calendar.DayColumns>
    </Calendar.TimeGrid>
  </Calendar.Root>
)