briannesbitt/Carbon · error · InvalidArgumentException
$anchorDay must be greater than 0
Error message
$anchorDay must be greater than 0
What it means
setAnchorDay() (reached via the anchorDay parameter of addUnit/add/sub or OverflowMode::AnchorDay) pins a day-of-month that should survive month arithmetic: after adding months the result lands on min(anchorDay, daysInMonth). Day-of-month is 1-based in PHP, so any anchorDay below 1 (0 or negative) is a programming/input error and throws InvalidArgumentException before any date math runs.
Source
Thrown at src/Carbon/Traits/Units.php:600
return $stringValue;
}
if (str_contains($stringValue, 'E')) {
return number_format($value, 14, '.', '');
}
return $stringValue;
}
/**
* Set current day of the instance to the passed value if it exits in the
* current month, else set current day to the last day of the month.
*/
public function setAnchorDay(int $anchorDay): static
{
if ($anchorDay < 1) {
throw new InvalidArgumentException('$anchorDay must be greater than 0');
}
return $this->day(min($anchorDay, $this->daysInMonth));
}
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 {View on GitHub (pinned to b13f05955d)
Solutions
- Pass null (omit anchorDay) when there is no anchor instead of 0
- Coerce with max(1, $anchorDay) or reject values < 1 at the input boundary
- When the source is zero-based (e.g. JS weekday), add 1 before passing
- Catch InvalidArgumentException only as a last-resort guard around truly external input
Example fix
// before
$anchor = (int) ($data['pay_day'] ?? 0);
$date->addUnit('month', 1, anchorDay: $anchor);
// after
$anchor = isset($data['pay_day']) ? max(1, (int) $data['pay_day']) : null;
$date->addUnit('month', 1, anchorDay: $anchor); Defensive patterns
Strategy: validation
Validate before calling
$anchorDay = $data['pay_day'] ?? null;
$anchorDay = $anchorDay === null ? null : max(1, min(31, (int) $anchorDay));
$date->addUnit('month', 1, anchorDay: $anchorDay); Type guard
function isValidAnchorDay(mixed $value): bool
{
return $value === null || (is_int($value) && $value >= 1 && $value <= 31);
} Try / catch
try {
$date->addUnit('month', 1, anchorDay: $anchor);
} catch (\InvalidArgumentException $e) {
// only reachable with unvalidated external input - map to 422
throw new ValidationException($e->getMessage());
} Prevention
- Use null for 'no anchor', never 0 - day-of-month is 1-based
- When converting zero-based sources (JS weekdays, indices), add 1 before passing
- Cast optional inputs with isset + (int), not (int) on a possibly-null value
When it happens
Trigger: ->addUnit('month', 1, anchorDay: 0); ->add(1, 'month', overflow: null, anchorDay: -5); anchorDay computed as $date->day - $x where the subtraction hits 0; (int) cast of unvalidated request input ('day' => '0'); an optional field defaulted through (int) null (= 0) instead of null.
Common situations: Off-by-one bugs when deriving the anchor from a zero-based source (JS Date.getDay, array indices, ISO week day numbers); treating 'no anchor' as 0 instead of omitting the argument; form/API input where day 0 means 'not provided' but is forwarded as an int.
Related errors
- $anchorDay can be set only $overflow = OverflowMode::AnchorD
- Invalid constructor parameters.
- Unknown unit '$unit'.
- Invalid serialized value: $value
- Invalid unit for real timestamp add/sub: '$unit'
AI-assisted analysis of briannesbitt/Carbon@b13f05955d (2026-08-17).
Data as JSON: /api/errors/068483ebc4477a8c.
Report an issue: GitHub.