phalcon/cphalcon · error · Phalcon\Acl\Exceptions\ForbiddenWildcard

The component name cannot be '*'

Error message

The component name cannot be '*'

What it means

The Redis session adapter (Phalcon\Session\Adapter\Redis) optionally serializes session access: with 'lockingEnabled' => true, read() must acquire a per-session Redis lock (SET <prefix><id>-lock <token> NX EX <lockExpiry>) before reading. It retries lockRetries times pausing lockWaitTime microseconds between attempts (defaults: 100 x 50,000us = ~5s budget); if every attempt fails because another request holds the lock, AdapterRuntimeError is thrown with the lock key name.

Source

Thrown at phalcon/Acl/Component.zep:26

 * file that was distributed with this source code.
 */

namespace Phalcon\Acl;

use Phalcon\Acl\Exceptions\ForbiddenWildcard;

/**
 * This class defines component entity and its description
 */
class Component extends AbstractElement implements ComponentInterface
{
    /**
     * Phalcon\Acl\Component constructor
     */
    public function __construct( string name, string description = null)
    {
        if unlikely name === "*" {
            throw new ForbiddenWildcard("component");
        }

        let this->name = name,
            this->description = description;
    }
}

View on GitHub (pinned to b7419de9cd)

Solutions

  1. Raise the wait budget above the longest concurrent request: increase 'lockRetries' and/or 'lockWaitTime'.
  2. Reduce session lock hold time: shorten requests, or serialize same-session AJAX from the browser side.
  3. Set 'lockExpiry' above the longest expected request so the lock survives, and remember a request running past lockExpiry silently loses it (per the class docblock) — tune both together.
  4. Unblock immediately by deleting the stuck key '<prefix><session-id>-lock' in Redis.
  5. If strict serialization is not required, disable it: 'lockingEnabled' => false.

Example fix

// before
$session = new \Phalcon\Session\Adapter\Redis(
    $factory,
    ['lockingEnabled' => true] // defaults: 100 retries x 50ms ~= 5s budget
);

// after — budget tuned for slow pages + parallel XHR
$session = new \Phalcon\Session\Adapter\Redis(
    $factory,
    [
        'lockingEnabled' => true,
        'lockRetries'    => 400,    // 400 x 50ms = 20s wait budget
        'lockWaitTime'   => 50000,
        'lockExpiry'     => 60,     // > longest expected request
    ]
);
Defensive patterns

Strategy: retry

Try / catch

use Phalcon\Session\Adapter\Exceptions\AdapterRuntimeError;

session_start(); // triggers read() -> lock acquisition

try {
    $session = new \Phalcon\Session\Adapter\Redis($factory, $sessionOptions);
    $manager = new \Phalcon\Session\Manager();
    $manager->setAdapter($session)->start();
} catch (AdapterRuntimeError $e) {
    if (str_contains($e->getMessage(), 'Could not acquire the session lock')) {
        // lock contention: back off briefly and retry once; longer term,
        // raise lockRetries/lockWaitTime above the longest concurrent request
        usleep(250000);
        $manager->start();
    } else {
        throw $e;
    }
}

Prevention

When it happens

Trigger: Two concurrent requests sharing a session id where the first holds the lock longer than lockRetries * lockWaitTime (default ~5s): parallel AJAX calls while a slow page renders; a previous request crashed after acquiring but before close()/destroy() released the lock; a lockExpiry (default 30s) longer than the wait budget with a stuck key.

Common situations: Dashboards firing many simultaneous same-session XHRs; long-running reports blocking reads; leaked lock keys after PHP-FPM worker kills; locking enabled in production but not in dev, so it only explodes under real concurrency.

Related errors


AI-assisted analysis of phalcon/cphalcon@b7419de9cd (2026-08-21). Data as JSON: /api/errors/035152c6a1bd0fdb. Report an issue: GitHub.