{"record":{"id":"cbebdd57b3f01046","repo":"stride3d/stride","slug":"could-not-find-compositions-for-expression-foreach","errorCode":null,"errorMessage":"Could not find compositions for expression [{@foreach.Collection}]","messagePattern":"Could not find compositions for expression \\[(.+?)\\]","errorType":"exception","errorClass":"InvalidOperationException","httpStatus":null,"severity":"error","filePath":"sources/shaders/Stride.Shaders.Compilers/SDSL/ShaderMixer.cs","lineNumber":847,"sourceCode":"        var depth = 1;\n        var endIndex = index;\n        while (depth > 0 && ++endIndex < buffer.Count - 1)\n        {\n            if (buffer[endIndex].Op == Op.OpForeachSDSL)\n                depth++;\n            else if (buffer[endIndex].Op == Op.OpForeachEndSDSL)\n                depth--;\n        }\n        endIndex++;\n\n        if (depth > 0)\n            throw new InvalidOperationException(\"Could not find end of foreach instruction\");\n\n        // Check the variable (both in current mixin node or in stage)\n        // TODO: should we register Compositions by ID in the global context instead, to avoid having to check Stage all the time?)\n        if (!mixinNode.CompositionArrays.TryGetValue(@foreach.Collection, out var compositions)\n            && (mixinNode.Stage == null || !mixinNode.Stage.CompositionArrays.TryGetValue(@foreach.Collection, out compositions)))\n            throw new InvalidOperationException($\"Could not find compositions for expression [{@foreach.Collection}]\");\n\n        // Extract foreach buffer (with the foreach start/end)\n        var foreachBuffer = buffer[index..endIndex];\n        buffer.RemoveRange(index, foreachBuffer.Count, false);\n\n        var foreachBufferCopy = new List<OpData>();\n        // Note: Make sure we replace the OpForeachSDSL with a first OpNop, so that if a for() loop works fine and don't miss an instruction without having to do index--\n        foreachBufferCopy.Add(new OpData(new OpNop().InstructionMemory));\n        for (int j = 0; j < compositions.Length; ++j)\n        {\n            var idRemapping = new Dictionary<int, int>();\n\n            // Setup variable for iterator access\n            var accessChain = new OpAccessChain(0, context.Bound++, @foreach.Collection, [context.CompileConstant(j).Id]);\n            foreachBufferCopy.Add(new(accessChain.InstructionMemory));\n            idRemapping.Add(@foreach.ResultId, accessChain);\n\n            // Do a first pass to find all IDs (OpBranch might point to OpLabel which are defined further)","sourceCodeStart":829,"sourceCodeEnd":865,"githubUrl":"https://github.com/stride3d/stride/blob/96fad776d210c221682aac1ccdf4c79dc046fc38/sources/shaders/Stride.Shaders.Compilers/SDSL/ShaderMixer.cs#L829-L865","documentation":"The SDSL mixer expands foreach instructions over composition arrays (streams/buffer compositions). Before expansion it verifies the collection name exists as a CompositionArray in the current mixin node or its Stage. If the name cannot be resolved, the foreach cannot be expanded and mixing fails.","triggerScenarios":"ExpandForeach (called from ProcessMemberAccessAndForeach) hits a foreach whose Collection name is absent from both mixinNode.CompositionArrays and mixinNode.Stage.CompositionArrays.","commonSituations":"foreach over a composition/stream name misspelled in the shader; the composition was removed or renamed in a base shader; using foreach on a variable that is not a composition array; shader layering (stage) not set up so the stage-side lookup is skipped.","solutions":["Declare the collection as a composition array in the mixin (e.g. stream/composition group) so it can be iterated.","Fix the foreach collection name to match the declared composition array.","Verify the base shader that declares the composition is actually composed into this shader.","Check whether the composition lives in a Stage and that the Stage is attached (Stage != null)."],"exampleFix":"// before\nforeach (var c in myCompositions) { ... }\n// after (declare it in the shader first)\nstream MyCompositions[4]; // or composition array declaration\nforeach (var c in MyCompositions) { ... }","handlingStrategy":"validation","validationCode":"// Before expanding foreach, verify the composition array exists\nbool hasComposition = mixinNode.CompositionArrays.ContainsKey(collectionName)\n    || (mixinNode.Stage?.CompositionArrays.ContainsKey(collectionName) ?? false);\nif (!hasComposition)\n    throw new InvalidOperationException($\"'{collectionName}' is not a declared composition array\");","typeGuard":null,"tryCatchPattern":"try { var mixed = mixer.Process(...); }\ncatch (InvalidOperationException ex) when (ex.Message.Contains(\"Could not find compositions\"))\n{\n    log.Error(ex, \"foreach over a name that is not a composition array; check stream/composition declarations\");\n    throw;\n}","preventionTips":["Only use foreach on names declared as composition/stream arrays in the shader.","Keep collection declarations in the base shader that also contains the foreach.","Compile shaders as part of CI so bad foreach targets fail at build time, not runtime.","Avoid renaming composition arrays without updating foreach sites; grep for foreach usages first."],"tags":["shader","sdsl","foreach","composition"],"backgroundTag":"resource-not-found","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"}