laravel/framework · error · LogicException

Circular alias reference for [{$abstract}].

Error message

Circular alias reference for [{$abstract}].

What it means

Thrown by Container::getAlias when following the alias chain produces a cycle: while resolving aliases, the same abstract is encountered twice, indicating the alias map points back to itself transitively (A -> B -> A). Without this guard getAlias would loop infinitely.

Source

Thrown at src/Illuminate/Container/Container.php:1681

    {
        return $this->bindings;
    }

    /**
     * Get the alias for an abstract if available.
     *
     * @param  string  $abstract
     * @return string
     *
     * @throws \LogicException
     */
    public function getAlias($abstract)
    {
        $seen = [];

        while (isset($this->aliases[$abstract])) {
            if (isset($seen[$abstract])) {
                throw new LogicException("Circular alias reference for [{$abstract}].");
            }

            $seen[$abstract] = true;

            $abstract = $this->aliases[$abstract];
        }

        return $abstract;
    }

    /**
     * Get the extender callbacks for a given type.
     *
     * @param  string  $abstract
     * @return array
     */
    protected function getExtenders($abstract)
    {

View on GitHub (pinned to bd6b5437e6)

Solutions

  1. Inspect all alias() calls and break the cycle so the chain terminates at a non-aliased abstract.
  2. Use $container->getAlias($abstract) in a debug dump to find which abstract closes the loop.
  3. Register aliases in a single canonical place (e.g. one service provider) to prevent conflicting registrations.

Example fix

// before
$app->alias(Contract::class, 'payment');
$app->alias('payment', Contract::class); // back-reference

// after
$app->alias(Contract::class, 'payment');
// remove the reverse alias; resolve via either 'payment' or Contract::class
Defensive patterns

Strategy: validation

Validate before calling

function aliasChainTerminates(\Illuminate\Container\Container $c, string $start, int $max = 50): bool
{
    $seen = []; $cur = $start;
    while (isset($c->getAliases()[$cur] ?? null) || $c->isAlias($cur)) {
        if (isset($seen[$cur])) return false;
        $seen[$cur] = true; $cur = $c->getAlias($cur);
        if (--$max < 0) return false;
    }
    return true;
}

Try / catch

try {
    $container->getAlias($abstract);
} catch (\LogicException $e) {
    if (str_contains($e->getMessage(), 'Circular alias')) {
        // break the cycle by re-registering a single canonical alias
    }
    throw $e;
}

Prevention

When it happens

Trigger: Registering $container->alias('A','B') then $container->alias('B','A'), or any sequence where aliases form a cycle; resolving any abstract that hits the cyclic chain. Note: the direct self-alias case (A->A) is caught earlier by the 'aliased to itself' guard; this catches A->B->A and longer loops.

Common situations: Programmatic alias setup from config that accidentally creates a loop; renaming classes and updating aliases in two places that now reference each other; service provider ordering producing cross-referential aliases.

Related errors


AI-assisted analysis of laravel/framework@bd6b5437e6 (2026-08-06). Data as JSON: /data/errors/c28869605db06cbd.json. Report an issue: GitHub.