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

Incompatible ACL snapshot version '{version}'; expected '{SN

Error message

Incompatible ACL snapshot version '{version}'; expected '{SNAPSHOT_VERSION}'

What it means

QueueDestinationGuard::assertQueue() enforces a single rule shared by producers (send) and contexts (createConsumer): the destination must implement Phalcon\Contracts\Queue\Queue. Any other DestinationInterface implementation — notably a Topic — triggers InvalidDestinationException, with the {action} slot in 'This transport can only {action} a Queue destination' telling you whether the send path or the consume path rejected it.

Source

Thrown at phalcon/Acl/Adapter/Storage.zep:92

        let data = this->storage->get(this->key);

        if typeof data === "object" {
            let data = this->normalizeToArray(data);
        }

        if typeof data !== "array" {
            return false;
        }

        if !isset data["version"] {
            return false;
        }

        let version = data["version"];

        if version != self::SNAPSHOT_VERSION {
            throw new InvalidSnapshot(
                "Incompatible ACL snapshot version '" . version .
                "'; expected '" . self::SNAPSHOT_VERSION . "'"
            );
        }

        if unlikely (
            typeof data["access"] !== "array" ||
            typeof data["accessList"] !== "array" ||
            typeof data["components"] !== "array" ||
            typeof data["componentsNames"] !== "array" ||
            typeof data["roles"] !== "array" ||
            typeof data["roleInherits"] !== "array"
        ) {
            throw new InvalidSnapshot("Malformed ACL snapshot structure");
        }

        let rebuiltRoles = [];
        for name, description in data["roles"] {

View on GitHub (pinned to b7419de9cd)

Solutions

  1. Create the destination with createQueue('name') instead of createTopic('name') for this transport.
  2. If you genuinely need publish/subscribe semantics, switch to a transport that implements topics.
  3. Type-check the destination before calling send()/createConsumer() when destinations are dynamic.

Example fix

// before
$destination = $context->createTopic('orders');
$producer->send($destination, $message); // InvalidDestinationException

// after
$destination = $context->createQueue('orders');
$producer->send($destination, $message);
Defensive patterns

Strategy: type-guard

Type guard

function isQueueDestination(\Phalcon\Contracts\Queue\Destination $destination): bool
{
    return $destination instanceof \Phalcon\Contracts\Queue\Queue;
}

// usage
if (!isQueueDestination($destination)) {
    throw new \InvalidArgumentException(
        'Queue-only transport: destination must be created with createQueue()'
    );
}
$producer->send($destination, $message);

Try / catch

try {
    $producer->send($destination, $message);
} catch (\Phalcon\Queue\Exceptions\InvalidDestinationException $e) {
    // message names the action: 'send to' or 'consume from'
    // rebuild the destination with createQueue() and retry
    $producer->send($context->createQueue($destination->getName()), $message);
}

Prevention

When it happens

Trigger: $producer->send($context->createTopic('news'), $message) on a queue-only transport, or $context->createConsumer($topic) — the topic object passes the DestinationInterface type hint but fails the instanceof Queue check.

Common situations: Code written against queue-interop expecting topic support (RabbitMQ-style pub/sub) reused on a queue-only backend; variable destinations routed from config where 'topic' was selected; refactoring from an AMQP transport.

Related errors


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