briannesbitt/Carbon · error · InvalidPeriodDateException
Invalid start date.
Error message
Invalid start date.
What it means
setStartDate() converts its input through the period's date class ::make(); values that cannot be parsed into a date make ::make() return null, which triggers InvalidPeriodDateException('Invalid start date.') (src/Carbon/CarbonPeriod.php:1369). Infinite markers accepted by isInfiniteDate() (INF / end-of-time style values) are exempt.
Source
Thrown at src/Carbon/CarbonPeriod.php:1369
$self->carbonRecurrences = $recurrences === INF ? INF : (int) $recurrences;
return self::addFilterOrHandleChangedParameters($self, static::RECURRENCES_FILTER);
}
/**
* Change the period start date.
*
* @param DateTime|DateTimeInterface|string $date
* @param bool|null $inclusive
*
* @throws InvalidPeriodDateException
*
* @return static
*/
public function setStartDate(mixed $date, ?bool $inclusive = null): static
{
if (!$this->isInfiniteDate($date) && !($date = ([$this->dateClass, 'make'])($date, $this->timezone))) {
throw new InvalidPeriodDateException('Invalid start date.');
}
$self = $this->copyIfImmutable();
$self->startDate = $date;
if ($inclusive !== null) {
$self = $self->toggleOptions(static::EXCLUDE_START_DATE, !$inclusive);
}
$self->syncNativePeriod();
return $self;
}
/**
* Change the period end date.
*
* @param DateTime|DateTimeInterface|string|null $dateView on GitHub (pinned to b13f05955d)
Solutions
- Validate or parse before: Carbon::hasFormat($date, 'Y-m-d') then pass, or wrap with Carbon::parse() and handle its failure
- Pass a DateTimeInterface you already constructed
- Reject empty/invalid input at the system boundary instead of letting the period constructor see it
Example fix
// before
$period->setStartDate($request->input('from')); // 'whenever'
// after
$from = $request->input('from');
if (is_string($from) && Carbon::hasFormat($from, 'Y-m-d')) {
$period->setStartDate(Carbon::parse($from));
} else {
abort(422, 'from must be a Y-m-d date');
} Defensive patterns
Strategy: validation
Validate before calling
if (\is_string($date) && Carbon::make($date) === null) {
throw new InvalidArgumentException('Unparseable start date: '.$date);
}
$period->setStartDate($date); Try / catch
try {
$period->setStartDate($input);
} catch (\Carbon\Exceptions\InvalidPeriodDateException $e) {
throw new ValidationException('start: must be a valid date', 0, $e);
} Prevention
- Constrain user input to an explicit format and validate with Carbon::hasFormat() at the boundary
- Pass DateTimeInterface objects you already parsed; keep strings out of internals
- Treat empty strings as missing values and convert to null/default before use
When it happens
Trigger: ->setStartDate('not a date'); ->setStartDate('31/02/2021'); ->setStartDate(''); ->setStartDate($garbage) where $garbage is an unparseable string or unrelated value.
Common situations: Free-text date fields forwarded without validation; localized or day-first formats Carbon does not guess (d/m/Y); empty request parameters coerced to empty strings.
Related errors
- Invalid end date.
- Argument 1 passed to {$class}::{$method}() must be an instan
- Invalid ISO 8601 specification: {$iso}.
- $anchorDay parameter must not be set for $mode OverflowMode:
- You must specify $end or $recurrences but not both
AI-assisted analysis of briannesbitt/Carbon@b13f05955d (2026-08-17).
Data as JSON: /api/errors/62b05a15005e94fb.
Report an issue: GitHub.