{"record":{"id":"bddb3e68346fde77","repo":"stride3d/stride","slug":"composition-variable-variable-key-is-not-an-array-but-had","errorCode":null,"errorMessage":"Composition variable {variable.Key} is not an array but had {compositionMixins.Length} entries","messagePattern":"Composition variable (.+?) is not an array but had (.+?) entries","errorType":"exception","errorClass":"InvalidOperationException","httpStatus":null,"severity":"error","filePath":"sources/shaders/Stride.Shaders.Compilers/SDSL/ShaderMixer.cs","lineNumber":347,"sourceCode":"        {\n            foreach (var variable in shader.Variables)\n            {\n                if (variable.Value.Type is PointerType pointer && pointer.BaseType is ShaderSymbol or ArrayType { BaseType: ShaderSymbol })\n                {\n                    // Every piece of context needed to act on this is in scope, and the dictionary\n                    // indexer would throw a bare \"The given key was not present\" instead.\n                    if (!mixinSource.Compositions.TryGetValue(variable.Key, out var compositionMixins))\n                        throw new InvalidOperationException(\n                            $\"No composition was supplied for '{variable.Key}', declared as '{variable.Value.Type}' by shader '{shader.ShaderName}', \"\n                            + $\"while merging the mixin node '{currentCompositionPath ?? \"<root>\"}' (root: {mixinNode.IsRoot}). \"\n                            + $\"That node only has [{string.Join(\", \", mixinSource.Compositions.Keys)}]. \"\n                            + $\"A `stage compose` is the usual cause: the shader declaring it was promoted to this node, \"\n                            + $\"but its value was supplied at a nested composition path and nothing carried it up.\");\n\n                    var isCompositionArray = pointer.BaseType is ArrayType { BaseType: ShaderSymbol };\n\n                    if (!isCompositionArray && compositionMixins.Length != 1)\n                        throw new InvalidOperationException($\"Composition variable {variable.Key} is not an array but had {compositionMixins.Length} entries\");\n\n                    var compositionResults = new MixinNode[compositionMixins.Length];\n                    for (int i = 0; i < compositionMixins.Length; ++i)\n                    {\n                        var localKey = variable.Key;\n                        if (isCompositionArray)\n                            localKey += $\"[{i}]\";\n                        // TODO: Review: it seems like Stride compose variable the opposite way that we expect\n                        //       Let's change it so that it becomes {currentCompositionPath}.{localKey}!\n                        var compositionPath = currentCompositionPath != null ? $\"{localKey}.{currentCompositionPath}\" : localKey;\n                        compositionResults[i] = MergeMixinNode(globalContext, context, buffer, compositionMixins[i], mixinNode.IsRoot ? mixinNode : mixinNode.Stage, compositionPath);\n                    }\n\n                    if (isCompositionArray)\n                        mixinNode.CompositionArrays.Add(variable.Value.Id, compositionResults);\n                    else\n                        mixinNode.Compositions.Add(variable.Value.Id, compositionResults[0]);\n                }","sourceCodeStart":329,"sourceCodeEnd":365,"githubUrl":"https://github.com/stride3d/stride/blob/96fad776d210c221682aac1ccdf4c79dc046fc38/sources/shaders/Stride.Shaders.Compilers/SDSL/ShaderMixer.cs#L329-L365","documentation":"A composition variable not declared as an array (pointer base type is a plain ShaderSymbol, not ArrayType of ShaderSymbol) must receive exactly one mixin. If the supplied composition has more than one entry, MergeMixinNode throws InvalidOperationException because multiple shaders cannot fill a non-array composition slot.","triggerScenarios":"Assigning multiple shader sources to a composition key whose declaration is `compose ShaderClass` (not `compose ShaderClass[]`); pushing several mixins into a non-array compose channel during tree building.","commonSituations":"Programmatically appending multiple effects to a single compose slot; mis-declaring `compose` without `[]` while feeding a list; copy-pasted array composition code applied to a scalar composition.","solutions":["Declare the variable as an array composition (`compose MyShader[]`) if multiple entries are intended","Supply exactly one mixin for the non-array composition key","Deduplicate or pick one source when building the composition list"],"exampleFix":"// before\nshaderClass: `compose ShadingModel Shader;` // with 2 entries supplied\n// after\nshaderClass: `compose ShadingModel[] Shader;` // or supply a single entry","handlingStrategy":"validation","validationCode":"if (!isDeclaredAsArray && compositionEntries.Count != 1)\n    throw new InvalidOperationException(\"Non-array composition needs exactly one entry\");","typeGuard":"bool IsArrayComposition(PointerType p) => p.BaseType is ArrayType { BaseType: ShaderSymbol };","tryCatchPattern":"try { result = mixer.MergeSDSL(tree); }\ncatch (InvalidOperationException ex) when (ex.Message.Contains(\"is not an array but had\")) {\n    reduceToOneCompositionEntry(key);\n}","preventionTips":["Match composition cardinality to the declaration (`compose X` vs `compose X[]`)","Deduplicate mixin lists before assigning to compose slots","Review programmatic composition building code"],"tags":["shaders","sdsl","composition","arrays"],"backgroundTag":"invalid-argument-value","analyzedSha":"96fad776d210c221682aac1ccdf4c79dc046fc38","analyzedAt":"2026-09-14T02:59:31.279Z","contentChangedAt":"2026-09-14T02:59:31.279Z","schemaVersion":2},"datasetVersion":"2026-09-15T23:17:13.987Z"}