The Magento multi-store bug every AI description generator has โ€” and how we fixed it Common architectural flaw in Magento 2 AI content modules where product descriptions are saved in the default store scope instead of per store view, causing multilingual catalogs to be overwritten with a single language. The author built an MIT-licensed, open-source solution that iterates through each store view and uses a four-provider AI abstraction (OpenAI, Claude, Gemini, and free Groq) to generate and save descriptions correctly. The framework supports multiple input sources and output destinations, with practical recommendations to validate prompts using Groq's free tier before deploying paid models for production. A client came to us with 8,000 SKUs across four store views โ€” English, Dutch, German, French. Descriptions were either copied from supplier PDFs or missing entirely. The fix was obviously AI generation. The less obvious problem was that every existing module we evaluated had the same architectural bug . So we built our own, made it MIT-licensed, and put it on Packagist. This post is about the bug, the fix, and the four-provider abstraction including a free one we shipped on top. ๐Ÿ”— Originally published on https://angeo.dev/magento-2-ai-product-description-generator/ The bug nobody talks about Most Magento 2 AI content modules call this: php $product = $this- productRepository- get $sku, editMode: true ; $product- setCustomAttribute 'description', $generated ; $this- productRepository- save $product ; Looks fine. It isn't. Without an explicit $storeId , this loads and saves in the default scope โ€” Magento's global, store-view-independent fallback. When you save back, you overwrite every store view at once . The Dutch store gets English descriptions. The German store gets English descriptions. The French store gets English descriptions. This is not a configuration problem. The multi-store architecture works correctly โ€” the tooling ignores it. Writing to the default scope is simpler to implement than writing per store. Every commercial module we tested took the simpler path. The right way: php // Load the product in the target store scope $product = $this- productRepository- get $sku, false, $storeId ; $product- setCustomAttribute 'description', $generated ; // Save with explicit store scope Magento Catalog\Model\Product\Action $this- productService- updateAttributes $sku, $generated, $storeId ; The difference is one parameter. The architectural cost is iterating stores around your generation loop. The data cost of skipping it is silently corrupting your multi-language catalog. The framework around the fix The store-scope fix is the boring part. The interesting part is everything you need around it. โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ Angeo Multi-Store AI Content Framework โ”‚ โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค โ”‚ SKU Source โ”‚ Store Iteration โ”‚ AI Provider โ”‚ โ”‚ โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ โ”‚ โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ โ”‚ โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ โ”‚ โ”‚ Catalog โ”‚ Store 1 EN โ”‚ OpenAI โ”‚ โ”‚ G.Sheets โ”‚ Store 2 NL โ”‚ Claude โ”‚ โ”‚ CLI --sku โ”‚ Store 3 DE โ”‚ Gemini โ”‚ โ”‚ โ”‚ Store 4 FR โ”‚ Groq free โ”‚ โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค โ”‚ Content Pipeline โ”‚ โ”‚ load sku, storeId โ†’ prompt โ†’ generate โ†’ save โ”‚ โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค โ”‚ Output โ”‚ โ”‚ Magento DB ยท Local CSV ยท Google Sheets API v4 โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ Four layers: - Provider Layer โ€” uniform interface across OpenAI, Claude, Gemini, Groq - Store Iteration Layer โ€” resolves all active store views before processing any SKUs - Content Pipeline โ€” for each store ร— SKU: load in scope โ†’ build prompt โ†’ generate โ†’ save in scope - I/O Layer โ€” reads SKUs from catalog, Google Sheet, or CLI; writes to Magento DB, CSV, and Google Sheets The provider abstraction is the part most people will copy. One interface: interface AiProviderInterface { public function generate string $system, string $user : string; } Wired in di.xml :