briannesbitt/Carbon · error · InvalidArgumentException

$anchorDay can be set only $overflow = OverflowMode::AnchorD

Error message

$anchorDay can be set only $overflow = OverflowMode::AnchorDay

What it means

getOverflowMode() resolves the overflow parameter shared by addUnit/add/sub: passing anchorDay implicitly selects OverflowMode::AnchorDay behavior, and if you simultaneously pass an explicit overflow (true, false, OverflowMode::Overflow or NoOverflow) the two instructions contradict each other and InvalidArgumentException is thrown. Only anchorDay + null/AnchorDay overflow, or overflow without anchorDay, are consistent states.

Source

Thrown at src/Carbon/Traits/Units.php:623

    private static function disallowDecimalPart(mixed $value): void
    {
        if (((float) $value) !== ((float) (int) $value)) {
            throw new InvalidArgumentException(
                'Interval objects cannot be multiplied by a non-integer value.',
            );
        }
    }

    private function getOverflowMode(
        OverflowMode|bool|null $overflow = null,
        ?int $anchorDay = null,
    ): ?OverflowMode {
        if ($anchorDay !== null) {
            $overflow ??= OverflowMode::AnchorDay;

            if ($overflow !== OverflowMode::AnchorDay) {
                throw new InvalidArgumentException(
                    '$anchorDay can be set only $overflow = OverflowMode::AnchorDay',
                );
            }
        }

        return match ($overflow) {
            true => OverflowMode::Overflow,
            false => OverflowMode::NoOverflow,
            default => $overflow,
        };
    }

    private function shouldUnitOverflow(string $unit): bool
    {
        $ucUnit = ucfirst($unit).'s';

        return $this->{'local'.$ucUnit.'Overflow'} ?? static::{'shouldOverflow'.$ucUnit}();
    }

View on GitHub (pinned to b13f05955d)

Solutions

  1. Drop the explicit overflow argument when using anchorDay - anchor mode already implies no-overflow clamping
  2. If you must pass it, pass the exact value OverflowMode::AnchorDay
  3. Rewrite positional calls with named arguments to stop the bool and anchorDay from colliding (anchorDay: 31 makes the intent unambiguous)
  4. In shared helpers, resolve the conflict yourself: forward overflow only when anchorDay is null

Example fix

// before
$date->addUnit('month', 1, false, 31);

// after
$date->addUnit('month', 1, anchorDay: 31);
Defensive patterns

Strategy: validation

Validate before calling

// Forward overflow only when no anchor is requested - the two options conflict
return $this->base->addUnit(
    $unit,
    $value,
    $anchorDay !== null ? OverflowMode::AnchorDay : $overflow,
    $anchorDay,
);

Type guard

function resolvesToConsistentOptions(OverflowMode|bool|null $overflow, ?int $anchorDay): bool
{
    return $anchorDay === null
        || $overflow === null
        || $overflow === OverflowMode::AnchorDay;
}

Try / catch

try {
    $date->addUnit('month', 1, $overflow, $anchorDay);
} catch (\InvalidArgumentException $e) {
    if (str_contains($e->getMessage(), 'AnchorDay')) {
        $date->addUnit('month', 1, anchorDay: $anchorDay); // drop conflicting overflow
    } else {
        throw $e;
    }
}

Prevention

When it happens

Trigger: ->addUnit('month', 1, overflow: true, anchorDay: 31); ->add(1, 'quarter', false, 15); ->addUnit('month', 1, OverflowMode::NoOverflow, anchorDay: 31); refactors that started passing anchorDay but left an old positional bool overflow argument in place; factories forwarding both parameters from callers unaware of the constraint.

Common situations: Adding anchorDay to legacy call sites that already passed overflow positionally (the bool silently occupies the overflow slot and now conflicts); wrappers/builders exposing both knobs and forwarding them verbatim; copy-pasting OverflowMode::Overflow from adjacent non-anchor calls.

Related errors


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