phalcon/cphalcon · error · Phalcon\Tag\Exception

A dependency injection container is required to access the '

Error message

A dependency injection container is required to access the 'url' service

What it means

Phalcon\Tag's URL-related helpers (linkTo, image, stylesheets etc.) resolve the 'url' shared service from the default DI container; the resolved UrlInterface is cached statically in Tag::$urlService. When no default container exists, getUrlService() throws Phalcon\Tag\Exception ("A dependency injection container is required to access the 'url' service"). FactoryDefault registers 'url' (Phalcon\Mvc\Url) automatically — hitting this means Tag ran without a booted container.

Source

Thrown at phalcon/Tag.zep:507

    public static function getTitleSeparator() -> string
    {
        return (string) self::documentTitleSeparator;
    }

    /**
     * Returns a URL service from the default DI
     */
    public static function getUrlService() -> <UrlInterface>
    {
        var url, container;

        let url = self::urlService;

        if typeof url != "object" {
            let container = self::getDI();

            if container === null {
                throw new Exception(
                    "A dependency injection container is required to access the 'url' service"
                );
            }

            let url = <UrlInterface> container->getShared("url"),
                self::urlService = url;
        }

        return url;
    }

    /**
     * Every helper calls this function to check whether a component has a
     * predefined value using Phalcon\Tag::setDefault() or value from $_POST
     *
     * @phpstan-param tag_parameters $parameters
     */
    public static function getValue(var name, array params = []) -> mixed

View on GitHub (pinned to b7419de9cd)

Solutions

  1. Register a container: Tag::setDI($di) with $di->setShared('url', \Phalcon\Mvc\Url::class), or Di::setDefault(new FactoryDefault())
  2. Register 'url' explicitly if you use a bare Phalcon\Di\Di instead of FactoryDefault
  3. Keep tag helper usage inside the bootstrapped application lifecycle

Example fix

// before (no container)
echo \Phalcon\Tag::linkTo(['https://example.com', 'Example']); // throws

// after
$di = new \Phalcon\Di\Di();
$di->setShared('url', \Phalcon\Mvc\Url::class);
\Phalcon\Di\Di::setDefault($di);
\Phalcon\Tag::setDI($di);

echo \Phalcon\Tag::linkTo(['https://example.com', 'Example']);
Defensive patterns

Strategy: validation

Validate before calling

use Phalcon\Di\Di;
use Phalcon\Mvc\Url;
use Phalcon\Tag;

if (null === Di::getDefault()) {
    $di = new Di();
    $di->setShared('url', Url::class);
    Di::setDefault($di);
    Tag::setDI($di);
}

echo \Phalcon\Tag::linkTo(['https://example.com', 'Example']);

Type guard

function hasTagUrlContainer(): bool
{
    return null !== \Phalcon\Di\Di::getDefault()
        || null !== \Phalcon\Tag::getDI();
}

Try / catch

try {
    echo \Phalcon\Tag::linkTo(['user/list', 'Users']);
} catch (\Phalcon\Tag\Exception $e) {
    if (str_contains($e->getMessage(), "'url' service")) {
        echo '<a href="/user/list">Users</a>'; // manual fallback
    } else {
        throw $e;
    }
}

Prevention

When it happens

Trigger: Calling Tag::linkTo() / Tag::image() / Tag::stylesheetLink() in a unit test or CLI script; after Di::reset(); once Tag::$urlService has been cached by an earlier successful call, later calls no longer need the container (order-dependent).

Common situations: Testing view helpers in isolation; micro-apps or workers that render partials; custom bootstrap that never registers the container or calls Di::setDefault.

Related errors


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