briannesbitt/Carbon · error · InvalidArgumentException

$anchorDay parameter must not be set for $mode OverflowMode:

Error message

$anchorDay parameter must not be set for $mode OverflowMode::{$mode->name}

What it means

CarbonPeriod::monthly()/quarterly()/yearly() accept an $anchorDay (day of month to pin, e.g. 'the 31st') together with an OverflowMode controlling what happens when a month has fewer days. An anchor day only has meaning with the default OverflowMode::AnchorDay; the NoOverflow and Overflow modes define their own day handling, so passing a non-null $anchorDay alongside them is contradictory and rejected in getStartAndEndForCyclePeriod() (src/Carbon/CarbonPeriod.php:576).

Source

Thrown at src/Carbon/CarbonPeriod.php:576

     */
    protected static function addMissingParts(string $source, string $target): string
    {
        $pattern = '/'.preg_replace('/\d+/', '[0-9]+', preg_quote($target, '/')).'$/';

        $result = preg_replace($pattern, $target, $source, 1, $count);

        return $count ? $result : $target;
    }

    private static function getStartAndEndForCyclePeriod(
        DateTimeInterface|string|int|null $start = null,
        DateTimeInterface|string|int|null $end = null,
        ?int $recurrences = null,
        ?int $anchorDay = null,
        OverflowMode $mode = OverflowMode::AnchorDay,
    ): array {
        if ($anchorDay !== null && $mode !== OverflowMode::AnchorDay) {
            throw new InvalidArgumentException(
                '$anchorDay parameter must not be set for $mode OverflowMode::'.$mode->name,
            );
        }

        if ($end !== null && $recurrences !== null) {
            throw new InvalidArgumentException(
                'You must specify $end or $recurrences but not both',
            );
        }

        if (\is_int($start)) {
            $start = CarbonImmutable::createFromTimestamp($start);
        } elseif (\is_string($start)) {
            $start = CarbonImmutable::parse($start);
        }

        $start ??= CarbonImmutable::now();

View on GitHub (pinned to b13f05955d)

Solutions

  1. Drop the anchor day (pass null) when using NoOverflow or Overflow modes
  2. Keep the anchor day and stay on the default mode (omit $mode, i.e. OverflowMode::AnchorDay)
  3. Use named arguments to remove positional mix-ups: CarbonPeriod::monthly(start: $start, end: $end, mode: OverflowMode::NoOverflow)

Example fix

// before
$period = CarbonPeriod::monthly('2021-01-31', '2021-12-31', null, 31, OverflowMode::NoOverflow);

// after — anchor day only with AnchorDay mode
$period = CarbonPeriod::monthly('2021-01-31', '2021-12-31', null, 31);
// or keep the mode and drop the anchor
$period = CarbonPeriod::monthly('2021-01-31', '2021-12-31', null, null, OverflowMode::NoOverflow);
Defensive patterns

Strategy: validation

Validate before calling

if ($anchorDay !== null && $mode !== \Carbon\Enums\OverflowMode::AnchorDay) {
    throw new InvalidArgumentException(
        'anchorDay may only be combined with OverflowMode::AnchorDay'
    );
}
$period = CarbonPeriod::monthly($start, $end, $recurrences, $anchorDay, $mode);

Prevention

When it happens

Trigger: CarbonPeriod::monthly($start, $end, null, 31, OverflowMode::NoOverflow); the same with OverflowMode::Overflow — any cycle-constructor call where the 4th argument is non-null while $mode is not OverflowMode::AnchorDay.

Common situations: Refactoring a monthly/quarterly/yearly billing or reminder schedule and carrying the anchor-day argument over while switching the overflow strategy; positional-argument mistakes where a value meant for $recurrences lands in $anchorDay.

Related errors


AI-assisted analysis of briannesbitt/Carbon@b13f05955d (2026-08-17). Data as JSON: /api/errors/8b9f73e1a08cfe0f. Report an issue: GitHub.