{"record":{"id":"2125acaa0a86498e","repo":"symfony/symfony","slug":"circular-reference-detected-while-creating-asset-f","errorCode":null,"errorMessage":"Circular reference detected while creating asset for \"%s\": \"%s\".","messagePattern":"Circular reference detected while creating asset for \"(.+?)\": \"(.+?)\"\\.","errorType":"exception","errorClass":"CircularAssetsException","httpStatus":null,"severity":"error","filePath":"src/Symfony/Component/AssetMapper/Factory/MappedAssetFactory.php","lineNumber":42,"sourceCode":"class MappedAssetFactory implements MappedAssetFactoryInterface\n{\n    private const PREDIGESTED_REGEX = '/-([0-9a-zA-Z]{7,128}\\.digested)/';\n    private const PUBLIC_DIGEST_LENGTH = 7;\n\n    private array $assetsCache = [];\n    private array $assetsBeingCreated = [];\n\n    public function __construct(\n        private readonly PublicAssetsPathResolverInterface $assetsPathResolver,\n        private readonly AssetMapperCompiler $compiler,\n        private readonly string $vendorDir,\n    ) {\n    }\n\n    public function createMappedAsset(string $logicalPath, string $sourcePath): ?MappedAsset\n    {\n        if (isset($this->assetsBeingCreated[$logicalPath])) {\n            throw new CircularAssetsException($this->assetsCache[$logicalPath], \\sprintf('Circular reference detected while creating asset for \"%s\": \"%s\".', $logicalPath, implode(' -> ', $this->assetsBeingCreated).' -> '.$logicalPath));\n        }\n        $this->assetsBeingCreated[$logicalPath] = $logicalPath;\n\n        if (!isset($this->assetsCache[$logicalPath])) {\n            $isVendor = $this->isVendor($sourcePath);\n            $asset = new MappedAsset($logicalPath, $sourcePath, $this->assetsPathResolver->resolvePublicPath($logicalPath), isVendor: $isVendor);\n            $this->assetsCache[$logicalPath] = $asset;\n\n            $content = $this->compileContent($asset);\n            [$digest, $isPredigested] = $this->getDigest($asset, $content);\n\n            $asset = new MappedAsset(\n                $asset->logicalPath,\n                $asset->sourcePath,\n                $asset->publicPathWithoutDigest,\n                $this->getPublicPath($asset, $content),\n                $content,\n                $digest,","sourceCodeStart":24,"sourceCodeEnd":60,"githubUrl":"https://github.com/symfony/symfony/blob/698e28026c22cf35d032cdb6e800db48b1535790/src/Symfony/Component/AssetMapper/Factory/MappedAssetFactory.php#L24-L60","documentation":"MappedAssetFactory recursively compiles dependencies while building a MappedAsset. It tracks logical paths currently being created in assetsBeingCreated; if createMappedAsset() is re-entered for a logicalPath already in that set, it throws CircularAssetsException, printing the dependency chain. The exception exposes getIncompleteMappedAsset() so callers can break the cycle and continue.","triggerScenarios":"Asset A's compilation pulls in asset B (via a compiler dependency) which transitively requires A again before A's entry is finalized; createMappedAsset() detects logicalPath in assetsBeingCreated and throws at line 42.","commonSituations":"Genuine top-level circular ES module imports (a code smell even in browsers); a self-referencing import inside a comment that the JS parser treats as executable; custom compilers that add cross dependencies.","solutions":["Read the 'A -> B -> ... -> A' chain in the message to identify the cycle's nodes.","Break the cycle by removing one direction of the import or deferring it with a dynamic import().","Extract the shared logic into a third module both sides import instead of each other.","If the cycle only appears in comments/non-executable code, make sure it is not parsed as a real import."],"exampleFix":"// before: a.js <-> b.js import each other at top level\n// a.js\nimport { b } from './b.js';\n// b.js\nimport { a } from './a.js';\n\n// after: shared module removes the cycle\n// shared.js exports the common code; a.js and b.js both import './shared.js'","handlingStrategy":"try-catch","validationCode":"// Run a cycle detector over the import graph before building.\n// e.g. `npx madge --circular assets/` in CI to catch top-level circular imports early.","typeGuard":null,"tryCatchPattern":"// Break the cycle gracefully using the incomplete asset carried by the exception.\ntry {\n    $asset = $factory->createMappedAsset($logicalPath, $sourcePath);\n} catch (\\Symfony\\Component\\AssetMapper\\Exception\\CircularAssetsException $e) {\n    $asset = $e->getIncompleteMappedAsset();\n    error_log('Circular asset reference, using incomplete asset: '.$e->getMessage());\n}","preventionTips":["Avoid top-level circular imports; refactor shared code into a third module.","Defer one side of a cycle with dynamic import().","Run a circular-dependency detector (madge, dependency-cruiser) in CI."],"tags":["asset-mapper","javascript","circular-dependency","dependency-graph"],"analyzedSha":"698e28026c22cf35d032cdb6e800db48b1535790","analyzedAt":"2026-08-06T23:40:49.025Z","schemaVersion":2},"datasetVersion":"2026-08-07T03:17:09.362Z"}