# Conflicts
URL: https://pro.rv-grid.com/guides/event-scheduler/scheduling-rules/conflicts/
Source: src/content/docs/guides/event-scheduler/scheduling-rules/conflicts.mdx
Description: Configure warning and blocking conflict rules for overlapping scheduler events and invalid mutations.
Conflict detection can mark overlaps, warn users, or block changes through the mutation lifecycle. This is also how you forbid events outside working hours: configure working time with `calendars` or `eventSchedulerAvailability`, then make the resulting `outside-availability` conflict blocking.

For a detailed explanation of how availability and calendar inputs become `outside-availability` and `blocked-time`, see the [Scheduling Rules overview](../).

```ts
grid.eventScheduler = {
  view: 'resourceTimeline',
  weekStartDate: '2026-06-08',
  conflicts: {
    enabled: true,
    policy: 'mark',
    scope: 'same-resource',
    minDurationMinutes: 30,
    rules: {
      overlap: 'error',
      'outside-availability': 'error',
      'blocked-time': 'error',
    },
  },
  validateMutation: (detail) => {
    const blocking = detail.conflicts?.some((conflict) => conflict.severity === 'error');
    if (blocking) {
      return 'This change creates a blocking scheduling conflict.';
    }
  },
};
```

`policy` sets the default behavior for all conflict types:

| Policy | Behavior |
| :-- | :-- |
| `mark` | Show conflicts as warnings and allow the mutation unless a specific rule is blocking. |
| `prevent` | Treat conflicts as blocking errors by default. |
| `allow` | Ignore conflicts unless a specific rule overrides the type. |

`rules` override the default policy per conflict type. Use `error` to reject the mutation, `warning` to mark it but allow it, `confirm` when your product wants a confirmation flow, and `ignore` to hide the conflict.

Common blocking rules:

```ts
conflicts: {
  enabled: true,
  policy: 'mark',
  rules: {
    overlap: 'error',
    'outside-availability': 'error',
    'blocked-time': 'error',
    'invalid-duration': 'error',
  },
}
```

`outside-availability` is raised when an event falls outside calendar working days/hours or outside explicit `kind: 'working'` availability intervals. `blocked-time` is raised when an event overlaps explicit blocked, holiday, or break availability. `nonWorkingTime` alone is visual and does not create blocking conflicts.

## Preventing Mutations

Conflict rules are the declarative way to stop invalid changes. When a conflict resolves to `severity: 'error'`, the scheduler rejects the mutation before it updates `eventSchedulerEvents`.

For business rules that need code, use the same cancelable event pattern as RevoGrid editing. Listen to `event-scheduler-before-event-change` and call `preventDefault()` before the local commit:

```ts
grid.addEventListener('event-scheduler-before-event-change', (event) => {
  const hasBlockingConflict = event.detail.conflicts?.some((conflict) =>
    conflict.severity === 'error' &&
    event.detail.eventId !== null &&
    conflict.eventIds.includes(event.detail.eventId)
  );

  if (hasBlockingConflict || event.detail.event?.locked) {
    event.preventDefault();
  }
});
```

`validateMutation` is the config-level equivalent for synchronous validation. Return `false` or a string to reject the same pending mutation.

```ts
const eventScheduler = {
  validateMutation: (detail) => {
    if (detail.changes.resourceId === 'locked-room') {
      return 'That room cannot accept bookings.';
    }
  },
};
```

Do not use `event-scheduler-event-changed` for prevention. `event-scheduler-event-created`, `event-scheduler-event-changed`, and `event-scheduler-event-deleted` fire after the scheduler has accepted the mutation; use those events to persist or sync the emitted `detail.events` array.

Listen to conflict updates for side panels, badges, filters, or exports.

```ts
grid.addEventListener('event-scheduler-conflicts-updated', (event) => {
  renderConflictPanel(event.detail.conflicts);
});
```

Event blocks expose conflict classes and data attributes for custom styles.
