getgrav/grav · error · Pimple\Exception\InvalidServiceIdentifierException

Identifier "%s" does not contain an object definition.

Error message

Identifier "%s" does not contain an object definition.

What it means

Container::offsetGet() accepts a mixed offset because Container implements ArrayAccess, but it immediately requires the service ID to be a string. A non-string offset throws InvalidServiceIdentifierException; the exception text about an object definition is generic and can be misleading because the actual check at Container.php:125-127 is only is_string($id).

Source

Thrown at system/src/Pimple/Container.php:126

        }

        $this->values[$id] = $value;
        $this->keys[$id] = true;
    }

    /**
     * Gets a parameter or an object.
     *
     * @param string $id The unique identifier for the parameter or object
     *
     * @return mixed The value of the parameter or an object
     *
     * @throws UnknownIdentifierException If the identifier is not defined
     */
    public function offsetGet(mixed $id): mixed
    {
        if (!is_string($id)) {
            throw new InvalidServiceIdentifierException($id);
        }

        if (!isset($this->keys[$id])) {
            throw new UnknownIdentifierException($id);
        }

        if (
            isset($this->raw[$id])
            || !is_object($this->values[$id])
            || isset($this->protected[$this->values[$id]])
            || !method_exists($this->values[$id], '__invoke')
        ) {
            return $this->values[$id];
        }

        if (isset($this->factories[$this->values[$id]])) {
            return $this->values[$id]($this);
        }

View on GitHub (pinned to 6040efed04)

Solutions

  1. Check or cast the variable before access: use $container[(string) $id] only after confirming it is a non-empty string.
  2. Fail early when the configured service name is missing instead of passing null to the container.
  3. Store service names in configuration as quoted strings.
  4. Use a known literal service name when possible.

Example fix

// before
$id = $settings['service'] ?? null;
$service = $container[$id]; // Identifier "" does not contain...

// after
$id = $settings['service'] ?? null;
if (!is_string($id) || $id === '') {
    throw new InvalidArgumentException('settings.service must be a service ID string');
}
$service = $container[$id];
Defensive patterns

Strategy: type-guard

Validate before calling

if (!is_string($id)) {
    throw new InvalidArgumentException(sprintf('Container ID must be a string, got %s.', get_debug_type($id)));
}
$service = $container[$id];

Type guard

function isContainerServiceId(mixed $id): bool
{
    return is_string($id);
}

Try / catch

try {
    $service = $container[$id];
} catch (InvalidServiceIdentifierException $e) {
    throw new InvalidArgumentException(sprintf('Invalid dynamic container ID type: %s', get_debug_type($id)), 0, $e);
}

Prevention

When it happens

Trigger: Calling $container[$id] where $id is an integer, null, boolean, array, or object. Common shapes are $container[0], $container[$config['service'] ?? null], or an ID taken from a YAML map whose PHP key was converted to an integer.

Common situations: Dynamic service names loaded from configuration or request arrays, numeric YAML keys, a missing environment variable becoming null, or code written for a plain PHP array being reused against the DI container.

Related errors


AI-assisted analysis of getgrav/grav@6040efed04 (2026-08-17). Data as JSON: /api/errors/a68ba727878031af. Report an issue: GitHub.