briannesbitt/Carbon · error · UnreachableException

Could not find next valid date.

Error message

Could not find next valid date.

What it means

While iterating, CarbonPeriod advances the current date by the interval and re-validates it against the filters. If no candidate passes validation within NEXT_MAX_ATTEMPTS (1000) attempts, the iterator concludes the filters may never be satisfied and throws UnreachableException('Could not find next valid date.') instead of spinning forever (src/Carbon/CarbonPeriod.php:2651).

Source

Thrown at src/Carbon/CarbonPeriod.php:2651

    /**
     * Keep incrementing the current date until a valid date is found or the iteration is ended.
     *
     * @throws RuntimeException
     */
    protected function incrementCurrentDateUntilValid(): void
    {
        $attempts = 0;

        do {
            $this->carbonCurrent = $this->carbonCurrent->add(
                $this->dateInterval,
                $this->dateInterval->getStep() && $this->dateInterval->invert ? -1 : 1,
            );

            $this->validationResult = null;

            if (++$attempts > static::NEXT_MAX_ATTEMPTS) {
                throw new UnreachableException('Could not find next valid date.');
            }
        } while ($this->validateCurrentDate() === false);
    }

    /**
     * Call given macro.
     */
    protected function callMacro(string $name, array $parameters): mixed
    {
        $macro = static::$macros[$name];

        if ($macro instanceof Closure) {
            $boundMacro = @$macro->bindTo($this, static::class) ?: @$macro->bindTo(null, static::class);

            return ($boundMacro ?: $macro)(...$parameters);
        }

        return $macro(...$parameters);

View on GitHub (pinned to b13f05955d)

Solutions

  1. Align the start with the filter: start on a matching date, e.g. ->setStartDate($start->next(Carbon::SUNDAY)) before a isSunday filter with P1W
  2. Fix the filter so dates reachable by the interval can satisfy it, and unit-test it against a sample of candidates
  3. Step by a unit that can reach a match (P1D instead of P1W) and end iteration explicitly by returning CarbonPeriod::END_ITERATION from the filter when done

Example fix

// before
$period = CarbonPeriod::create('2021-01-04', 'P1W') // 2021-01-04 is a Monday
    ->filter(fn ($date) => $date->isSunday());
$period->next(); // Could not find next valid date

// after
$period = CarbonPeriod::create('2021-01-10', 'P1W') // a Sunday
    ->filter(fn ($date) => $date->isSunday());
Defensive patterns

Strategy: try-catch

Validate before calling

// Smoke-test the filter over the next candidates reachable by the interval
$probe = $period->getStartDate()->copy();
$satisfiable = false;
for ($i = 0; $i < 1000; $i++) {
    if ($filter($probe, false)) { $satisfiable = true; break; }
    $probe = $probe->add($period->getDateInterval());
}
if (!$satisfiable) {
    throw new InvalidArgumentException('Filter can never match the interval step');
}

Try / catch

try {
    $period->next(); // or foreach ($period as $date)
} catch (\Carbon\Exceptions\UnreachableException $e) {
    // filters and interval can never converge: abort schedule generation and report
    \Log::warning('Unsatisfiable period filter: '.$e->getMessage());
    return [];
}

Prevention

When it happens

Trigger: A filter that can never match: ->filter(fn ($date) => false); stepping P1W from a Monday while filtering ->isSunday() (the weekday never changes); a filter looking for an impossible date (Feb 30); a filter whose condition is expressed in the wrong direction for an inverted interval.

Common situations: Filter logic bugs (wrong comparison operator, inverted condition); misaligned start date combined with a weekly interval and a weekday filter; filter units that never coincide with the interval step; schedule generators fed impossible requirements.

Related errors


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