phalcon/cphalcon · error · ArgumentTypeRequired

Argument at position {} must have a type

Error message

Argument at position {} must have a type

What it means

Thrown by Phalcon\Di\Service\Builder::buildParameter() when a constructor/call argument defined in the array-based DI definition has no 'type' key. Every entry under 'arguments' must be an array describing HOW to resolve the value: ['type' => 'service'|'parameter'|'instance', ...]. Without the type the builder cannot decide whether to fetch a service, use a literal, or instantiate a class.

Source

Thrown at phalcon/Di/Service/Builder.zep:209

        }

        return instance;
    }

    /**
     * Resolves a constructor/call parameter
     *
     * @return mixed
     */
    private function buildParameter(<DiInterface> container, int position,  array argument)
    {
        var type, name, value, instanceArguments;

        /**
         * All the arguments must have a type
         */
        if unlikely !fetch type, argument["type"] {
            throw new ArgumentTypeRequired(position);
        }

        switch type {
            /**
             * If the argument type is 'service', we obtain the service from the
             * DI
             */
            case "service":
                if unlikely !fetch name, argument["name"] {
                    throw new MissingParameterKey("name", position);
                }

                return container->get(name);

            /**
             * If the argument type is 'parameter', we assign the value as it is
             */
            case "parameter":

View on GitHub (pinned to b7419de9cd)

Solutions

  1. Add a 'type' key to the failing argument: 'parameter' for literals, 'service' to resolve another DI service by name, 'instance' to construct a class (Phalcon 5 name; 'class' in Phalcon 3).
  2. Use the error position (0-based) to find the exact broken element in your 'arguments' array.
  3. If the value is a plain literal, switch the whole definition to a closure: $di->set('foo', function() { return new Foo('bar'); }) which skips array parsing entirely.
  4. Validate definitions at boot (see defense) so a mis-typed definition fails during a startup smoke test instead of at first resolve.

Example fix

// before
$di->set(
    'invoiceMailer',
    [
        'className' => \App\InvoiceMailer::class,
        'arguments' => [
            ['value' => 'notifications@example.com'],   // no type -> ArgumentTypeRequired
        ],
    ]
);

// after
$di->set(
    'invoiceMailer',
    [
        'className' => \App\InvoiceMailer::class,
        'arguments' => [
            ['type' => 'parameter', 'value' => 'notifications@example.com'],
        ],
    ]
);
Defensive patterns

Strategy: validation

Validate before calling

const DI_ARGUMENT_KEYS = ['service' => 'name', 'parameter' => 'value', 'instance' => 'className'];

function assertValidDiDefinition(array $definition, string $serviceId): void
{
    $args = $definition['arguments'] ?? [];
    foreach ($args as $position => $arg) {
        if (!is_array($arg) || !isset($arg['type'])) {
            throw new InvalidArgumentException(sprintf(
                'DI service "%s": argument %d has no "type"', $serviceId, $position
            ));
        }
    }
}

// at boot, after registering array definitions:
assertValidDiDefinition($di->getDefinition('foo') /* or your source array */, 'foo');

Type guard

function isDiArgumentDefinition(mixed $arg): bool
{
    if (!is_array($arg) || !isset($arg['type']) || !is_string($arg['type'])) {
        return false;
    }
    $required = ['service' => 'name', 'parameter' => 'value', 'instance' => 'className'];

    return array_key_exists($arg['type'], $required)
        && isset($arg[$required[$arg['type']]);
}

Try / catch

try {
    $service = $di->get('foo');
} catch (\Phalcon\Di\Exceptions\ArgumentTypeRequired $e) {
    // definition bug: log with the service name and fail loudly
    $logger->error('DI definition missing argument type: ' . $e->getMessage());
    throw $e;
}

Prevention

When it happens

Trigger: Calling $di->set('foo', ['className' => 'Foo', 'arguments' => [['value' => 'bar']]]) and then resolving the service via $di->get('foo'), $di['foo'], or injecting it into another service. The throw happens at resolve time, not at set() time, because the array definition is parsed lazily.

Common situations: Writing array DI definitions by hand and assuming arguments are plain values (the old string/positional style), migrating from a different container (Symfony/Pimple pass raw values), or copy-pasting a definition and deleting the type line. Also hit when 'arguments' is built dynamically and one element is a scalar/empty array.

Related errors


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