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).

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.

Appendix: 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)