nextcloud/server · error · TooManyRequests

Too many calendars created

Error message

Too many calendars created

What it means

Thrown by the CalDAV RateLimitingPlugin, which hooks the Sabre 'beforeBind' event and fires whenever a new calendar or subscription is created at a path like calendars/<user>/<name>. It calls the OC rate limiter with the identifier 'caldav-create-calendar', the app config rateLimitCalendarCreation (default 10) and rateLimitPeriodCalendarCreation (default 3600 seconds), and converts the resulting RateLimitExceededException into a TooManyRequests DAV exception (HTTP 429). It is an anti-flood guard so one authenticated user cannot create unbounded calendars in a short window.

Source

Thrown at apps/dav/lib/CalDAV/Security/RateLimitingPlugin.php:64

		}
		$user = $this->userManager->get($this->userId);
		if ($user === null) {
			// We only care about authenticated users here
			return;
		}

		$pathParts = explode('/', $path);
		if (count($pathParts) === 3 && $pathParts[0] === 'calendars') {
			// Path looks like calendars/username/calendarname so a new calendar or subscription is created
			try {
				$this->limiter->registerUserRequest(
					'caldav-create-calendar',
					$this->config->getValueInt('dav', 'rateLimitCalendarCreation', 10),
					$this->config->getValueInt('dav', 'rateLimitPeriodCalendarCreation', 3600),
					$user
				);
			} catch (RateLimitExceededException $e) {
				throw new TooManyRequests('Too many calendars created', 0, $e);
			}

			$calendarLimit = $this->config->getValueInt('dav', 'maximumCalendarsSubscriptions', 30);
			if ($calendarLimit === -1) {
				return;
			}
			$numCalendars = $this->calDavBackend->getCalendarsForUserCount('principals/users/' . $user->getUID());
			$numSubscriptions = $this->calDavBackend->getSubscriptionsForUserCount('principals/users/' . $user->getUID());

			if (($numCalendars + $numSubscriptions) >= $calendarLimit) {
				$this->logger->warning('Maximum number of calendars/subscriptions reached', [
					'calendars' => $numCalendars,
					'subscription' => $numSubscriptions,
					'limit' => $calendarLimit,
				]);
				throw new Forbidden('Calendar limit reached', 0);
			}
		}

View on GitHub (pinned to ecdeb153ff)

Solutions

  1. Space out or queue calendar creations and wait for the period (default 3600 s) to elapse before retrying
  2. Raise the limit for the affected instance: occ config:app:set dav rateLimitCalendarCreation --value 100
  3. Shrink the measurement window if a short burst is legitimate: occ config:app:set dav rateLimitPeriodCalendarCreation --value 600
  4. For bulk provisioning, bypass HTTP and use CalDavBackend/occ tooling server-side instead of repeated MKCALENDAR requests

Example fix

// before: tight loop hitting the CalDAV endpoint
foreach ($names as $name) {
    $client->request('MKCALENDAR', "/remote.php/dav/calendars/$user/$name/");
}

// after: throttle client-side and back off on 429
$created = 0;
foreach ($names as $name) {
    if ($created >= 10) { sleep(3600); $created = 0; }
    $resp = $client->request('MKCALENDAR', "/remote.php/dav/calendars/$user/$name/");
    if ($resp->getStatus() === 429) { sleep(3600); continue; }
    $created++;
}
Defensive patterns

Strategy: retry

Validate before calling

// Client-side sliding-window counter before any MKCALENDAR
const WINDOW_MS = 3600_000, MAX = 10; // mirror dav defaults
const stamps = [];
function mayCreateCalendar() {
  const now = Date.now();
  while (stamps.length && now - stamps[0] > WINDOW_MS) stamps.shift();
  return stamps.length < MAX;
}

Try / catch

try {
    await client.mkCalendar(`/remote.php/dav/calendars/${user}/${name}/`);
} catch (e) {
    if (e.status === 429) {
        // honor the configured period (default 3600 s) before retrying
        await sleep(rateLimitPeriodCalendarCreationMs);
        return client.mkCalendar(`/remote.php/dav/calendars/${user}/${name}/`);
    }
    throw e;
}

Prevention

When it happens

Trigger: Performing MKCALENDAR/MKCOL (any bind that creates a node) on a three-segment path calendars/<username>/<newname> more than 10 times within one hour as the same user; typical with migration/import scripts, provisioning loops, or CalDAV clients that delete-and-recreate calendars repeatedly.

Common situations: Bulk import or sync tools creating many calendars in a loop; automated e2e tests running against a real server with default limits; admins unaware of the rate limit seeing 429 responses in client logs after Nextcloud 27+ introduced the plugin.

Related errors


AI-assisted analysis of nextcloud/server@ecdeb153ff (2026-08-17). Data as JSON: /api/errors/f5b8a4b5ca61bbe3. Report an issue: GitHub.