{"record":{"id":"f9ad351c55a8a66e","repo":"phalcon/cphalcon","slug":"malformed-acl-snapshot-structure","errorCode":null,"errorMessage":"Malformed ACL snapshot structure","messagePattern":"Malformed ACL snapshot structure","errorType":"exception","errorClass":"Phalcon\\Acl\\Exceptions\\InvalidSnapshot","httpStatus":null,"severity":"critical","filePath":"phalcon/Acl/Adapter/Storage.zep","lineNumber":106,"sourceCode":"\n        let version = data[\"version\"];\n\n        if version != self::SNAPSHOT_VERSION {\n            throw new InvalidSnapshot(\n                \"Incompatible ACL snapshot version '\" . version .\n                \"'; expected '\" . self::SNAPSHOT_VERSION . \"'\"\n            );\n        }\n\n        if unlikely (\n            typeof data[\"access\"] !== \"array\" ||\n            typeof data[\"accessList\"] !== \"array\" ||\n            typeof data[\"components\"] !== \"array\" ||\n            typeof data[\"componentsNames\"] !== \"array\" ||\n            typeof data[\"roles\"] !== \"array\" ||\n            typeof data[\"roleInherits\"] !== \"array\"\n        ) {\n            throw new InvalidSnapshot(\"Malformed ACL snapshot structure\");\n        }\n\n        let rebuiltRoles = [];\n        for name, description in data[\"roles\"] {\n            let rebuiltRoles[name] = new Role(name, description);\n        }\n\n        let rebuiltComponents = [];\n        for name, description in data[\"components\"] {\n            let rebuiltComponents[name] = new Component(name, description);\n        }\n\n        let this->access                   = data[\"access\"],\n            this->accessList               = data[\"accessList\"],\n            this->components               = rebuiltComponents,\n            this->componentsNames          = data[\"componentsNames\"],\n            this->roles                    = rebuiltRoles,\n            this->roleInherits             = data[\"roleInherits\"],","sourceCodeStart":88,"sourceCodeEnd":124,"githubUrl":"https://github.com/phalcon/cphalcon/blob/b7419de9cd0a8a3f48441ead84c9f8415d463e25/phalcon/Acl/Adapter/Storage.zep#L88-L124","documentation":"RedisConnectionFactory::createContext() delegates connect/auth/select to Phalcon\\Storage\\Adapter\\Redis via getAdapter(); any StorageException raised there is rethrown as a generic Queue Exception carrying the storage layer's message and code, with the original chained as previous. So the message you see is the underlying Redis error — e.g. connection refused, AUTH failed, or an invalid database index — repackaged so the queue honors its single throwable contract.","triggerScenarios":"new RedisConnectionFactory($options)->createContext() with a wrong 'host'/'port', wrong 'auth' password or [user, password] ACL pair, or an 'index' beyond the server's configured databases; Redis down or unreachable.","commonSituations":"Env-driven config mismatches between environments; Redis enabling requirepass/ACL after launch; SELECT on index 15 when databases=8; failover where the old node is gone; stale persistent connections.","solutions":["Verify by hand from the app host: redis-cli -h <host> -p <port> -a <auth> -n <index> PING.","Fix the offending option: host, port, auth (password or [user, password]), or index.","Inspect getPrevious() on the caught exception for the exact StorageException cause when the message alone is ambiguous."],"exampleFix":"// before\n$factory = new RedisConnectionFactory(['host' => 'redis.internal', 'index' => 15]);\n$context = $factory->createContext(); // wraps StorageException (e.g. invalid DB index)\n\n// after\n$factory = new RedisConnectionFactory(\n    [\n        'host' => 'redis.internal',\n        'port' => 6379,\n        'auth' => [getenv('REDIS_USER'), getenv('REDIS_PASS')],\n        'index' => 0, // must be < the server's `databases` setting\n    ]\n);\n$context = $factory->createContext();","handlingStrategy":"try-catch","validationCode":"// Pre-flight the exact parameters the factory will use\n$redis = new \\Redis();\n$ok = $redis->connect($options['host'] ?? '127.0.0.1', $options['port'] ?? 6379);\nif ($ok && isset($options['auth'])) {\n    $ok = $redis->auth($options['auth']); // string or [user, pass] for ACL\n}\nif ($ok && isset($options['index'])) {\n    $ok = $redis->select((int) $options['index']);\n}\nif (!$ok) {\n    throw new \\RuntimeException('Redis queue pre-flight failed: ' . $redis->getLastError());\n}\n$redis->close();","typeGuard":null,"tryCatchPattern":"use Phalcon\\Queue\\Exceptions\\Exception as QueueException;\n\ntry {\n    $context = (new RedisConnectionFactory($options))->createContext();\n} catch (QueueException $e) {\n    // $e->getMessage() mirrors the storage-layer message (connect/auth/select)\n    $cause = $e->getPrevious(); // the original StorageException\n    $logger->error('Redis queue connect failed: ' . $e->getMessage(), [\n        'exception' => $cause,\n    ]);\n    throw $e;\n}","preventionTips":["Validate host/port/auth/index once at boot with a PING-style pre-flight.","Keep 'index' below the server's `databases` setting across all environments.","Log getPrevious() — the wrapped StorageException carries the precise Redis error."],"tags":["queue","redis","connection","auth","configuration"],"backgroundTag":"redis-connection-failure","analyzedSha":"b7419de9cd0a8a3f48441ead84c9f8415d463e25","analyzedAt":"2026-08-21T06:21:18.811Z","schemaVersion":2},"datasetVersion":"2026-08-21T11:28:35.574Z"}