hcengineering/platform · warning

Unable to calculate negative position ${pos}

Error message

Unable to calculate negative position ${pos}

What it means

getNegativePosition computes the Nth occurrence of a weekday within a month for BYDAY/BYSETPOS handling. If it iterates through the whole period without collecting Math.abs(pos) occurrences, the requested negative position does not exist in that month and the error is thrown.

Source

Thrown at plugins/calendar/src/utils.ts:139

    }
  }

  return false
}

function getNegativePosition (date: Date, weekday: string, pos: number): number | undefined {
  const lastDayOfMonth = new Date(date.getFullYear(), date.getMonth() + 1, 0).getDate()
  const occurrences = []
  for (let day = lastDayOfMonth; day >= 1; day--) {
    const tempDate = new Date(date.getFullYear(), date.getMonth(), day)
    if (getWeekday(tempDate) === weekday) {
      occurrences.push(day)
    }
    if (occurrences.length === Math.abs(pos)) {
      return occurrences.pop()
    }
  }
  throw new Error(`Unable to calculate negative position ${pos}`)
}

function generateMonthlyValues (rule: RecurringRule, currentDate: Date, from: Timestamp, to: Timestamp): Timestamp[] {
  const values: Timestamp[] = []
  const { count, endDate, interval } = rule
  let { byDay, byMonthDay, bySetPos } = rule
  let i = 0

  if (byDay == null && byMonthDay == null) {
    byMonthDay = [currentDate.getDate()]
  }

  while (true) {
    const next = new Date(currentDate).setMonth(currentDate.getMonth() + (interval ?? 1), 1)
    const end = new Date(new Date(currentDate).setMonth(currentDate.getMonth() + 1, 1))
    let date = currentDate
    const candidates: Date[] = []

View on GitHub (pinned to 63e28dc964)

Solutions

  1. Clamp/validate negative positions in rules to values that exist for every affected month (e.g. -1 to -4 for weekly positions)
  2. Catch the error in expansion code and skip non-existent occurrences for that month
  3. Normalize imported iCalendar BYSETPOS values against month feasibility at import

Example fix

// before: rule with bySetPos: -5 for MONTHLY on Monday, February throws
// after: clamp before expansion
const maxPos = Math.floor(31 / 7) // 4 weekly occurrences guaranteed
const pos = Math.max(rule.bySetPos, -maxPos)
Defensive patterns

Strategy: try-catch

Validate before calling

const possible = Math.floor(daysInMonth(currentDate) / 7)
if (Math.abs(rule.bySetPos ?? -1) > possible) {
  // position does not exist in this month — skip occurrence
}

Try / catch

try {
  day = getNegativePosition(rule, currentDate, pos)
} catch (err) {
  if ((err as Error).message.startsWith('Unable to calculate negative position')) {
    continue // occurrence does not exist in this month
  } else throw err
}

Prevention

When it happens

Trigger: A recurring rule requests e.g. the 5th-last Monday (pos=-5) of a month that has fewer than 5 Mondays, or byDay/byMonthDay data is inconsistent with the month being expanded.

Common situations: Recurrence rules imported from external calendars using large negative set positions; hand-crafted rules like 'last fifth weekday'; month-length edge cases (February) during event expansion.

Related errors


AI-assisted analysis of hcengineering/platform@63e28dc964 (2026-08-29). Data as JSON: /api/errors/41bdbdb044a48871. Report an issue: GitHub.